From 44ac939bccb1e9d5246c5d9858a51ec5aacec8a4 Mon Sep 17 00:00:00 2001 From: Phil Tsaryk Date: Mon, 29 Jan 2024 22:43:21 +0100 Subject: [PATCH 1/6] chore: integrate eslint, prettier, husky, lint-staged --- .eslintignore | 13 + .eslintrc.json | 30 + .gitattributes | 2 + .gitignore | 5 +- .husky/pre-commit | 4 + .lintstagedrc.json | 4 + .prettierignore | 13 + .prettierrc.json | 5 + package-lock.json | 4585 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 19 + 10 files changed, 4677 insertions(+), 3 deletions(-) create mode 100644 .eslintignore create mode 100644 .eslintrc.json create mode 100644 .gitattributes create mode 100644 .husky/pre-commit create mode 100644 .lintstagedrc.json create mode 100644 .prettierignore create mode 100644 .prettierrc.json create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..d9f6317a6 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,13 @@ +# disabled for all by default +* +!.*.js + +# enabled for spotify only +!/spotify/ +!/spotify/**/ +!/spotify/**/* + +# add more exceptions to enable for other plugins +# ... + +node_modules/ diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000..e359af12e --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,30 @@ +{ + "extends": ["eslint:recommended", "plugin:prettier/recommended"], + "parser": "@babel/eslint-parser", + "parserOptions": { + "requireConfigFile": false + }, + "env": { + "es6": true, + "node": true + }, + "rules": { + "camelcase": [ + "warn", + { "properties": "never", "ignoreDestructuring": true, "ignoreImports": true, "ignoreGlobals": true } + ], + "eqeqeq": "warn", + "new-cap": "warn", + "no-async-promise-executor": "off", + "no-console": "off", + "no-constant-condition": "off", + "no-path-concat": "warn", + "no-prototype-builtins": "off", + "no-redeclare": "warn", + "no-use-before-define": "off", + "no-sequences": "error", + "no-unused-vars": "warn", + "no-var": "warn", + "strict": "off" + } +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..8e2f781d3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +install.sh text=auto eol=lf +uninstall.sh text=auto eol=lf diff --git a/.gitignore b/.gitignore index ebe846a30..a44f6f588 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ +.idea/ +node_modules/ .DS_Store -*node_modules* -.gitignore -/.idea/* diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 000000000..d24fdfc60 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npx lint-staged diff --git a/.lintstagedrc.json b/.lintstagedrc.json new file mode 100644 index 000000000..bfc7c2383 --- /dev/null +++ b/.lintstagedrc.json @@ -0,0 +1,4 @@ +{ + "*.js": "eslint --fix", + "*.{json,md}": "prettier --write" +} diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..b48468f03 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,13 @@ +# disabled for all by default +* +!.*.* + +# enabled for spotify only +!/spotify/ +!/spotify/**/ +!/spotify/**/* + +# add more exceptions to enable for other plugins +# ... + +node_modules/ diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 000000000..30ef82464 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,5 @@ +{ + "endOfLine": "auto", + "printWidth": 120, + "singleQuote": true +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..c1bba5eff --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4585 @@ +{ + "name": "volumio-plugins-sources", + "version": "0.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "volumio-plugins-sources", + "version": "0.0.0", + "devDependencies": { + "@babel/eslint-parser": "^7.23.9", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "husky": "^8.0.3", + "lint-staged": "^14.0.1", + "prettier": "^3.2.4" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "peer": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz", + "integrity": "sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==", + "dev": true, + "peer": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.7", + "@babel/parser": "^7.23.6", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/eslint-parser": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.23.9.tgz", + "integrity": "sha512-xPndlO7qxiJbn0ATvfXQBjCS7qApc9xmKHArgI/FTEFxXas5dnjC/VqM37lfZun9dclRYcn+YQAr6uDFy0bB2g==", + "dev": true, + "dependencies": { + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || >=14.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0", + "eslint": "^7.5.0 || ^8.0.0" + } + }, + "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.23.6", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.8.tgz", + "integrity": "sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "peer": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", + "dev": true, + "peer": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz", + "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true, + "peer": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "dev": true, + "dependencies": { + "eslint-scope": "5.1.1" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", + "dev": true, + "dependencies": { + "type-fest": "^1.0.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true, + "dependencies": { + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001576", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001576.tgz", + "integrity": "sha512-ff5BdakGe2P3SQsMsiqmt1Lc8221NR1VzHj5jXN5vBny9A6fpze94HiVV/n7XRosOlsShJcvMv5mdnpjOGCEgg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "dev": true, + "dependencies": { + "restore-cursor": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/commander": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "peer": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.4.628", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.628.tgz", + "integrity": "sha512-2k7t5PHvLsufpP6Zwk0nof62yLOsCf032wZx7/q0mv8gwlXjhcxI3lz6f0jBr0GrnWKcm3burXzI3t5IrcdUxw==", + "dev": true, + "peer": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", + "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, + "node_modules/execa": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/husky": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", + "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", + "dev": true, + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/ignore": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "peer": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "peer": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "peer": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lint-staged": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-14.0.1.tgz", + "integrity": "sha512-Mw0cL6HXnHN1ag0mN/Dg4g6sr8uf8sn98w2Oc1ECtFto9tvRF7nkXGJRbx8gPlHyoR0pLyBr2lQHbWwmUHe1Sw==", + "dev": true, + "dependencies": { + "chalk": "5.3.0", + "commander": "11.0.0", + "debug": "4.3.4", + "execa": "7.2.0", + "lilconfig": "2.1.0", + "listr2": "6.6.1", + "micromatch": "4.0.5", + "pidtree": "0.6.0", + "string-argv": "0.3.2", + "yaml": "2.3.1" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/listr2": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz", + "integrity": "sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==", + "dev": true, + "dependencies": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^5.0.1", + "rfdc": "^1.3.0", + "wrap-ansi": "^8.1.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/log-update": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz", + "integrity": "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==", + "dev": true, + "dependencies": { + "ansi-escapes": "^5.0.0", + "cli-cursor": "^4.0.0", + "slice-ansi": "^5.0.0", + "strip-ansi": "^7.0.1", + "wrap-ansi": "^8.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "peer": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true, + "peer": true + }, + "node_modules/npm-run-path": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", + "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true, + "peer": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.4.tgz", + "integrity": "sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", + "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", + "dev": true + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true, + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "peer": true + }, + "node_modules/yaml": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", + "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true + }, + "@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, + "peer": true, + "requires": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "peer": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "peer": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "peer": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "peer": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "peer": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "peer": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "peer": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/compat-data": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "dev": true, + "peer": true + }, + "@babel/core": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz", + "integrity": "sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==", + "dev": true, + "peer": true, + "requires": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.7", + "@babel/parser": "^7.23.6", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + } + }, + "@babel/eslint-parser": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.23.9.tgz", + "integrity": "sha512-xPndlO7qxiJbn0ATvfXQBjCS7qApc9xmKHArgI/FTEFxXas5dnjC/VqM37lfZun9dclRYcn+YQAr6uDFy0bB2g==", + "dev": true, + "requires": { + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.1" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.23.6", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "peer": true + }, + "@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "peer": true, + "requires": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.22.15" + } + }, + "@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + } + }, + "@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "dev": true, + "peer": true + }, + "@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "peer": true + }, + "@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "dev": true, + "peer": true + }, + "@babel/helpers": { + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.8.tgz", + "integrity": "sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6" + } + }, + "@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "peer": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "peer": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "peer": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "peer": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "peer": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "peer": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "peer": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", + "dev": true, + "peer": true + }, + "@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + } + }, + "@babel/traverse": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz", + "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==", + "dev": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "dependencies": { + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "peer": true + } + } + }, + "@babel/types": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + } + }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + } + }, + "@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true + }, + "@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true + }, + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "peer": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "peer": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true, + "peer": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "dev": true, + "requires": { + "eslint-scope": "5.1.1" + }, + "dependencies": { + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + } + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true + }, + "@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-escapes": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", + "dev": true, + "requires": { + "type-fest": "^1.0.2" + }, + "dependencies": { + "type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true + } + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "dev": true, + "peer": true, + "requires": { + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001576", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001576.tgz", + "integrity": "sha512-ff5BdakGe2P3SQsMsiqmt1Lc8221NR1VzHj5jXN5vBny9A6fpze94HiVV/n7XRosOlsShJcvMv5mdnpjOGCEgg==", + "dev": true, + "peer": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "dev": true, + "requires": { + "restore-cursor": "^4.0.0" + } + }, + "cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "requires": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "commander": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "peer": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.4.628", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.628.tgz", + "integrity": "sha512-2k7t5PHvLsufpP6Zwk0nof62yLOsCf032wZx7/q0mv8gwlXjhcxI3lz6f0jBr0GrnWKcm3burXzI3t5IrcdUxw==", + "dev": true, + "peer": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "peer": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + } + }, + "eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "requires": {} + }, + "eslint-plugin-prettier": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", + "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" + } + }, + "eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + }, + "espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "requires": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + } + }, + "esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, + "execa": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fastq": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "requires": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "peer": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true + }, + "husky": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", + "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", + "dev": true + }, + "ignore": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "peer": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "peer": true + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "peer": true + }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true + }, + "lint-staged": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-14.0.1.tgz", + "integrity": "sha512-Mw0cL6HXnHN1ag0mN/Dg4g6sr8uf8sn98w2Oc1ECtFto9tvRF7nkXGJRbx8gPlHyoR0pLyBr2lQHbWwmUHe1Sw==", + "dev": true, + "requires": { + "chalk": "5.3.0", + "commander": "11.0.0", + "debug": "4.3.4", + "execa": "7.2.0", + "lilconfig": "2.1.0", + "listr2": "6.6.1", + "micromatch": "4.0.5", + "pidtree": "0.6.0", + "string-argv": "0.3.2", + "yaml": "2.3.1" + }, + "dependencies": { + "chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true + } + } + }, + "listr2": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz", + "integrity": "sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==", + "dev": true, + "requires": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^5.0.1", + "rfdc": "^1.3.0", + "wrap-ansi": "^8.1.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "log-update": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz", + "integrity": "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==", + "dev": true, + "requires": { + "ansi-escapes": "^5.0.0", + "cli-cursor": "^4.0.0", + "slice-ansi": "^5.0.0", + "strip-ansi": "^7.0.1", + "wrap-ansi": "^8.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "peer": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true, + "peer": true + }, + "npm-run-path": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", + "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", + "dev": true, + "requires": { + "path-key": "^4.0.0" + }, + "dependencies": { + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + } + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "requires": { + "mimic-fn": "^4.0.0" + } + }, + "optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "requires": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true, + "peer": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.4.tgz", + "integrity": "sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, + "punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "dependencies": { + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + } + } + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rfdc": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", + "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + } + } + }, + "string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "dev": true, + "requires": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "peer": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "peer": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "peer": true + }, + "yaml": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", + "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..205838df4 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "volumio-plugins-sources", + "version": "0.0.0", + "private": true, + "scripts": { + "format": "prettier --write '**/*.{json,md}' && npm run lint -- --fix", + "lint": "eslint .", + "prepare": "husky install" + }, + "devDependencies": { + "@babel/eslint-parser": "^7.23.9", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "husky": "^8.0.3", + "lint-staged": "^14.0.1", + "prettier": "^3.2.4" + } +} From aa90806ce05df6527ebbccc6f38c475377884c29 Mon Sep 17 00:00:00 2001 From: Phil Tsaryk Date: Wed, 31 Jan 2024 21:30:17 +0100 Subject: [PATCH 2/6] style: reformat files --- spotify/README.md | 6 +- spotify/UIConfig.json | 131 +- spotify/apiclient.js | 42 +- spotify/i18n/strings_ca.json | 27 +- spotify/i18n/strings_de.json | 27 +- spotify/i18n/strings_en.json | 86 +- spotify/i18n/strings_fr.json | 80 +- spotify/i18n/strings_it.json | 84 +- spotify/i18n/strings_nl.json | 86 +- spotify/index.js | 4658 ++++++++--------- spotify/install.sh | 21 +- spotify/package.json | 86 +- .../extendedSpotifyApi/fetchPagedData.js | 2 +- .../extendedSpotifyApi/rateLimitedCall.js | 2 +- spotify/wsclient.js | 10 +- 15 files changed, 2658 insertions(+), 2690 deletions(-) diff --git a/spotify/README.md b/spotify/README.md index cbe3489e1..8c0572b3c 100755 --- a/spotify/README.md +++ b/spotify/README.md @@ -1,15 +1,13 @@ # New Spotify Plugin - This repo serves as WIP for the new spotify plugin. It is currently in development and not ready for use. ## Installation -To prepare the environment for go and go librespot - +To prepare the environment for go and go librespot + ```bash cd librespot-go ./prepare-env.sh ./clone-and-prepare.sh ``` - diff --git a/spotify/UIConfig.json b/spotify/UIConfig.json index 94a2eba1e..10db26d0b 100755 --- a/spotify/UIConfig.json +++ b/spotify/UIConfig.json @@ -4,55 +4,64 @@ }, "sections": [ { - "id": "section_spotify", - "type": "section", - "label": "Spotify", + "id": "section_spotify", + "type": "section", + "label": "Spotify", "icon": "fa-spotify", - "content": [ - { - "id": "spotify_use", - "description": "TRANSLATE.OPEN_OR_INSTALL_SPOTIFY_DESCRIPTION", - "onClick": {"type":"openUrl", "url":"spotify://"} - } - ] - }, + "content": [ + { + "id": "spotify_use", + "description": "TRANSLATE.OPEN_OR_INSTALL_SPOTIFY_DESCRIPTION", + "onClick": { "type": "openUrl", "url": "spotify://" } + } + ] + }, { - "id": "section_account", - "element": "section", - "label": "TRANSLATE.SPOTIFY_BROWSING", - "hidden": false, - "content": [ - { - "id":"oauth", - "element": "button", - "label": "TRANSLATE.AUTHORIZE", - "description": "TRANSLATE.AUTHORIZE_PERSONAL_CONTENT", - "onClick": {"type":"oauth", "performerUrl":"http://oauth-performer.dfs.volumio.org/spotify","plugin":"music_service/spop", "scopes":[ - "user-modify-playback-state", - "user-read-playback-state", - "user-read-currently-playing", - "user-top-read", - "user-read-recently-played", - "user-read-private", - "user-read-email", - "user-library-read", - "playlist-read-private", - "playlist-read-collaborative", - "app-remote-control", - "streaming", - "user-follow-read" - ]} - }, - { - "id":"logout", - "element": "button", - "label": "TRANSLATE.LOGOUT", - "description": "TRANSLATE.LOGOUT", - "onClick": {"type":"emit", "message":"callMethod", "data":{"endpoint":"music_service/spop","method":"logout","data":""}}, - "hidden": true - } - ] - }, + "id": "section_account", + "element": "section", + "label": "TRANSLATE.SPOTIFY_BROWSING", + "hidden": false, + "content": [ + { + "id": "oauth", + "element": "button", + "label": "TRANSLATE.AUTHORIZE", + "description": "TRANSLATE.AUTHORIZE_PERSONAL_CONTENT", + "onClick": { + "type": "oauth", + "performerUrl": "http://oauth-performer.dfs.volumio.org/spotify", + "plugin": "music_service/spop", + "scopes": [ + "user-modify-playback-state", + "user-read-playback-state", + "user-read-currently-playing", + "user-top-read", + "user-read-recently-played", + "user-read-private", + "user-read-email", + "user-library-read", + "playlist-read-private", + "playlist-read-collaborative", + "app-remote-control", + "streaming", + "user-follow-read" + ] + } + }, + { + "id": "logout", + "element": "button", + "label": "TRANSLATE.LOGOUT", + "description": "TRANSLATE.LOGOUT", + "onClick": { + "type": "emit", + "message": "callMethod", + "data": { "endpoint": "music_service/spop", "method": "logout", "data": "" } + }, + "hidden": true + } + ] + }, { "id": "spotify_settings", "element": "section", @@ -63,12 +72,7 @@ }, "saveButton": { "label": "TRANSLATE.SAVE", - "data": [ - "bitrate", - "debug", - "normalisation_pregain", - "icon" - ] + "data": ["bitrate", "debug", "normalisation_pregain", "icon"] }, "content": [ { @@ -80,10 +84,11 @@ "value": 320, "label": "320 kbps" }, - "options": [{ - "value": 320, - "label": "320 kbps" - }, + "options": [ + { + "value": 320, + "label": "320 kbps" + }, { "value": 160, "label": "160 kbps" @@ -159,10 +164,11 @@ "value": "avr", "label": "AV Receiver" }, - "options": [{ - "value": "avr", - "label": "AV Receiver" - }, + "options": [ + { + "value": "avr", + "label": "AV Receiver" + }, { "value": "speaker", "label": "Speaker" @@ -174,5 +180,6 @@ ] } ] - }] + } + ] } diff --git a/spotify/apiclient.js b/spotify/apiclient.js index a942557f3..f47109f9d 100644 --- a/spotify/apiclient.js +++ b/spotify/apiclient.js @@ -2,25 +2,27 @@ var superagent = require('superagent'); var apiEndpoint = 'http://127.0.0.1:9879'; +superagent + .post(apiEndpoint + '/player/volume') + .accept('application/json') + .send({ volume: 50, volume_steps: 100 }) + .then((results) => { + console.log(results.body); + }) + .catch((err) => { + console.log(err); + }); -superagent.post(apiEndpoint + '/player/volume') - .accept('application/json') - .send({ 'volume': 50, 'volume_steps': 100 }) - .then((results) => { - console.log(results.body); - }).catch((err) => { - console.log(err); - }) +superagent + .get(apiEndpoint + '/status') + .accept('application/json') + .then((results) => { + console.log(results.body); + }); - -superagent.get(apiEndpoint + '/status') - .accept('application/json') - .then((results) => { - console.log(results.body); - }) - -superagent.get(apiEndpoint + '/player/volume') - .accept('application/json') - .then((results) => { - console.log(results.body); - }) \ No newline at end of file +superagent + .get(apiEndpoint + '/player/volume') + .accept('application/json') + .then((results) => { + console.log(results.body); + }); diff --git a/spotify/i18n/strings_ca.json b/spotify/i18n/strings_ca.json index e78e505f0..2d1c3a685 100755 --- a/spotify/i18n/strings_ca.json +++ b/spotify/i18n/strings_ca.json @@ -1,16 +1,15 @@ { - "SPOTIFY_USERNAME":"Nom Usuari Spotify", - "SPOTIFY_PASSWORD":"Contrasenya Spotify", - "HIGH_BITRATE":"Alta qualitat", - "SEARCH_RESULTS":"Numero de resultats", - "PLUGINS":"Last.fm", - "LAST_FM_USERNAME":"Usuari Last.fm ", - "LAST_FM_PASSWORD":"ContrasenyaLast.fm", - "SEARCH_SONGS_SECTION":"Cançons Spotify", - "SEARCH_ALBUMS_SECTION":"Albums Spotity", - "SEARCH_ARTISTS_SECTION":"Artistes Spotify", - "SPOTIFY_CONFIGURATION":"Configuració Spotify", - "SPOTIFY_ACCOUNT":"Compte Spotify", - "SAVE":"Guarda" - + "SPOTIFY_USERNAME": "Nom Usuari Spotify", + "SPOTIFY_PASSWORD": "Contrasenya Spotify", + "HIGH_BITRATE": "Alta qualitat", + "SEARCH_RESULTS": "Numero de resultats", + "PLUGINS": "Last.fm", + "LAST_FM_USERNAME": "Usuari Last.fm ", + "LAST_FM_PASSWORD": "ContrasenyaLast.fm", + "SEARCH_SONGS_SECTION": "Cançons Spotify", + "SEARCH_ALBUMS_SECTION": "Albums Spotity", + "SEARCH_ARTISTS_SECTION": "Artistes Spotify", + "SPOTIFY_CONFIGURATION": "Configuració Spotify", + "SPOTIFY_ACCOUNT": "Compte Spotify", + "SAVE": "Guarda" } diff --git a/spotify/i18n/strings_de.json b/spotify/i18n/strings_de.json index 4edb47535..5d18f4e99 100755 --- a/spotify/i18n/strings_de.json +++ b/spotify/i18n/strings_de.json @@ -1,16 +1,15 @@ { - "SPOTIFY_USERNAME":"Spotify Benutzername", - "SPOTIFY_PASSWORD":"Spotify Passwort", - "HIGH_BITRATE":"Hohe Qualität", - "SEARCH_RESULTS":"Anzahl der Suchergebnisse", - "PLUGINS":"Last.fm", - "LAST_FM_USERNAME":"Last.fm Benutzername", - "LAST_FM_PASSWORD":"Last.fm Passwort", - "SEARCH_SONGS_SECTION":"Spotify Lieder", - "SEARCH_ALBUMS_SECTION":"Spotity Alben", - "SEARCH_ARTISTS_SECTION":"Spotify Künstler", - "SPOTIFY_CONFIGURATION":"Spotify Einstellungen", - "SPOTIFY_ACCOUNT":"Spotify Konto", - "SAVE":"Speichern" - + "SPOTIFY_USERNAME": "Spotify Benutzername", + "SPOTIFY_PASSWORD": "Spotify Passwort", + "HIGH_BITRATE": "Hohe Qualität", + "SEARCH_RESULTS": "Anzahl der Suchergebnisse", + "PLUGINS": "Last.fm", + "LAST_FM_USERNAME": "Last.fm Benutzername", + "LAST_FM_PASSWORD": "Last.fm Passwort", + "SEARCH_SONGS_SECTION": "Spotify Lieder", + "SEARCH_ALBUMS_SECTION": "Spotity Alben", + "SEARCH_ARTISTS_SECTION": "Spotify Künstler", + "SPOTIFY_CONFIGURATION": "Spotify Einstellungen", + "SPOTIFY_ACCOUNT": "Spotify Konto", + "SAVE": "Speichern" } diff --git a/spotify/i18n/strings_en.json b/spotify/i18n/strings_en.json index 1d57cf419..7b172d8e2 100755 --- a/spotify/i18n/strings_en.json +++ b/spotify/i18n/strings_en.json @@ -1,45 +1,45 @@ { - "SPOTIFY":"Spotify", - "USERNAME":"Username", - "PASSWORD":"Password", - "SPOTIFY_USERNAME":"Spotify username", - "SPOTIFY_PASSWORD":"Spotify password", - "HIGH_BITRATE":"High quality", - "REFRESH_TOKEN":"Refresh Token", - "SEARCH_RESULTS":"Number of results", - "PLUGINS":"Last.fm", - "LAST_FM_USERNAME":"Last.fm username", - "LAST_FM_PASSWORD":"Last.fm password", - "SEARCH_SONGS_SECTION":"Spotify songs", - "SEARCH_ALBUMS_SECTION":"Spotity albums", - "SEARCH_ARTISTS_SECTION":"Spotify artists", - "SPOTIFY_CONFIGURATION":"Spotify Configuration", - "SPOTIFY_ACCOUNT":"Spotify account", - "SAVE":"Save", - "LOGIN":"Login", - "LOGOUT":"Logout", - "AUTHORIZE_PERSONAL_CONTENT":"To be able to browse your music, playlists and favourites, please authorize the application", - "AUTHORIZE_PERSONAL_CONTENT_INSTRUCTIONS":"One more step is needed to enable Spotfy: please authorize the application by clicking the Authorize Button and follow the required steps.", - "AUTHORIZE":"Authorize", - "SPOTIFY_LOGIN":"Spotify login", - "LOGGING_IN":"Logging in Spotify", - "LOGIN_SUCCESSFUL":"Spotify Login Successful", - "LOGOUT_SUCCESSFUL":"Successfully logged out from Spotify", - "SPOTIFY_LOGIN_FAILED":"Spotify Login Failed", - "SUCCESSFULLY_AUTHORIZED":"Successfully authorized the access", - "CHECK_USERNAME_AND_PASSWORD":"Please check username and password", - "PROVIDE_USERNAME_AND_PASSWORD":"Please provide username and password", - "MY_PLAYLISTS":"My Playlists", - "MY_ALBUMS":"My Albums", - "MY_TRACKS":"My Tracks", + "SPOTIFY": "Spotify", + "USERNAME": "Username", + "PASSWORD": "Password", + "SPOTIFY_USERNAME": "Spotify username", + "SPOTIFY_PASSWORD": "Spotify password", + "HIGH_BITRATE": "High quality", + "REFRESH_TOKEN": "Refresh Token", + "SEARCH_RESULTS": "Number of results", + "PLUGINS": "Last.fm", + "LAST_FM_USERNAME": "Last.fm username", + "LAST_FM_PASSWORD": "Last.fm password", + "SEARCH_SONGS_SECTION": "Spotify songs", + "SEARCH_ALBUMS_SECTION": "Spotity albums", + "SEARCH_ARTISTS_SECTION": "Spotify artists", + "SPOTIFY_CONFIGURATION": "Spotify Configuration", + "SPOTIFY_ACCOUNT": "Spotify account", + "SAVE": "Save", + "LOGIN": "Login", + "LOGOUT": "Logout", + "AUTHORIZE_PERSONAL_CONTENT": "To be able to browse your music, playlists and favourites, please authorize the application", + "AUTHORIZE_PERSONAL_CONTENT_INSTRUCTIONS": "One more step is needed to enable Spotfy: please authorize the application by clicking the Authorize Button and follow the required steps.", + "AUTHORIZE": "Authorize", + "SPOTIFY_LOGIN": "Spotify login", + "LOGGING_IN": "Logging in Spotify", + "LOGIN_SUCCESSFUL": "Spotify Login Successful", + "LOGOUT_SUCCESSFUL": "Successfully logged out from Spotify", + "SPOTIFY_LOGIN_FAILED": "Spotify Login Failed", + "SUCCESSFULLY_AUTHORIZED": "Successfully authorized the access", + "CHECK_USERNAME_AND_PASSWORD": "Please check username and password", + "PROVIDE_USERNAME_AND_PASSWORD": "Please provide username and password", + "MY_PLAYLISTS": "My Playlists", + "MY_ALBUMS": "My Albums", + "MY_TRACKS": "My Tracks", "MY_ARTISTS": "My Artists", - "MY_TOP_ARTISTS":"My Top Artists", - "MY_TOP_TRACKS":"My Top Tracks", - "MY_RECENTLY_PLAYED_TRACKS":"My Recently Played Tracks", - "FEATURED_PLAYLISTS":"Featured Playlists", - "WHATS_NEW":"What's New", - "GENRES_AND_MOODS":"Genres & Moods", - "MY_MUSIC":"My Music", + "MY_TOP_ARTISTS": "My Top Artists", + "MY_TOP_TRACKS": "My Top Tracks", + "MY_RECENTLY_PLAYED_TRACKS": "My Recently Played Tracks", + "FEATURED_PLAYLISTS": "Featured Playlists", + "WHATS_NEW": "What's New", + "GENRES_AND_MOODS": "Genres & Moods", + "MY_MUSIC": "My Music", "VOLSPOTCONNECTCONF": "Spotify Configuration", "VOLSPOTCONNECTACCOUNT": "Spotify account", "HIGHBITRATE": "Stream bitrate", @@ -59,12 +59,12 @@ "DESCGAPLESS": "Enable/Disable gapless playback between tracks", "AUTOPLAY": "Autoplay", "DESCAUTOPLAY": "Autoplay similar songs when your music ends ", - "OPEN_OR_INSTALL_SPOTIFY_TITLE":"Open or Install Spotify", - "OPEN_OR_INSTALL_SPOTIFY_DESCRIPTION":"To listen to Spotify, simply open or Install Spotify from any device in the network and select this device as output. Spotify Premium is required", + "OPEN_OR_INSTALL_SPOTIFY_TITLE": "Open or Install Spotify", + "OPEN_OR_INSTALL_SPOTIFY_DESCRIPTION": "To listen to Spotify, simply open or Install Spotify from any device in the network and select this device as output. Spotify Premium is required", "LOGIN_TO_ENABLE_BROWSING": "To browse your tracks, albums and playlists, login with your Spotify Credentials", "SPOTIFY_BROWSING": "Spotify Browsing", "LOGGED_IN_AS": "Logged-in as", "CONFIGURATION_SUCCESSFULLY_UPDATED": "Configuration has been successfully updated", "CONNECT_DEVICE_ICON_DOC": "Choose which icon will show the device on your Spotify app in Connect Mode", - "CONNECT_DEVICE_ICON":"Device Icon" + "CONNECT_DEVICE_ICON": "Device Icon" } diff --git a/spotify/i18n/strings_fr.json b/spotify/i18n/strings_fr.json index 70caf4f84..f13427360 100755 --- a/spotify/i18n/strings_fr.json +++ b/spotify/i18n/strings_fr.json @@ -1,42 +1,42 @@ { - "SPOTIFY":"Spotify", - "USERNAME":"Utlisateur", - "PASSWORD":"Mot de passe", - "SPOTIFY_USERNAME":"Nom d'utilisateur Spotify", - "SPOTIFY_PASSWORD":"Mot de passe Spotify", - "HIGH_BITRATE":"Haute qualité", - "REFRESH_TOKEN":"Rafraichir le jeton", - "SEARCH_RESULTS":"Nombre de résultats", - "PLUGINS":"Last.fm", - "LAST_FM_USERNAME":"Nom d'utilistateur Last.fm", - "LAST_FM_PASSWORD":"Mot de passe Last.fm", - "SEARCH_SONGS_SECTION":"Pistes Spotify", - "SEARCH_ALBUMS_SECTION":"Albums Spotity", - "SEARCH_ARTISTS_SECTION":"Artistes Spotify", - "SPOTIFY_CONFIGURATION":"Configuration de Spotify", - "SPOTIFY_ACCOUNT":"Compte Spotify", - "SAVE":"Sauver", - "LOGIN":"Connexion", - "LOGOUT":"Déconnexion", - "AUTHORIZE_PERSONAL_CONTENT":"Pour pouvoir parcourir vos playlistes, favoris et vos musique, vous devez autoriser le plugin", - "AUTHORIZE_PERSONAL_CONTENT_INSTRUCTIONS":"Encore une étape pour activer Spotfy: Cliquez sur 'Autoriser' et suivez les instructions", - "AUTHORIZE":"Autoriser", - "SPOTIFY_LOGIN":"Connexion à Spotify", - "LOGGING_IN":"Déconnexion de Spotify", - "LOGIN_SUCCESSFUL":"Connexion avec succès", - "LOGOUT_SUCCESSFUL":"Déconnexion de Spotify ok", - "SPOTIFY_LOGIN_FAILED":"Echec de la connexion à Spotify", - "SUCCESSFULLY_AUTHORIZED":"Autorisation acceptée", - "CHECK_USERNAME_AND_PASSWORD":"Merci de vérifier votre nom d'utilisateur et mot de passe", - "PROVIDE_USERNAME_AND_PASSWORD":"Merci d'entrer votre nom d'utilisateur et mot de passe", - "MY_PLAYLISTS":"Mes Playlistes", - "MY_ALBUMS":"Mes Albums", - "MY_TRACKS":"Mes pistes", - "MY_TOP_ARTISTS":"Mes Top Artistes", - "MY_TOP_TRACKS":"Mes Top pistes", - "MY_RECENTLY_PLAYED_TRACKS":"Mes pistes jouées récemment", - "FEATURED_PLAYLISTS":"En vedette", - "WHATS_NEW":"Quoi de neuf", - "GENRES_AND_MOODS":"Genres & Ambiances", - "MY_MUSIC":"Ma Musique" + "SPOTIFY": "Spotify", + "USERNAME": "Utlisateur", + "PASSWORD": "Mot de passe", + "SPOTIFY_USERNAME": "Nom d'utilisateur Spotify", + "SPOTIFY_PASSWORD": "Mot de passe Spotify", + "HIGH_BITRATE": "Haute qualité", + "REFRESH_TOKEN": "Rafraichir le jeton", + "SEARCH_RESULTS": "Nombre de résultats", + "PLUGINS": "Last.fm", + "LAST_FM_USERNAME": "Nom d'utilistateur Last.fm", + "LAST_FM_PASSWORD": "Mot de passe Last.fm", + "SEARCH_SONGS_SECTION": "Pistes Spotify", + "SEARCH_ALBUMS_SECTION": "Albums Spotity", + "SEARCH_ARTISTS_SECTION": "Artistes Spotify", + "SPOTIFY_CONFIGURATION": "Configuration de Spotify", + "SPOTIFY_ACCOUNT": "Compte Spotify", + "SAVE": "Sauver", + "LOGIN": "Connexion", + "LOGOUT": "Déconnexion", + "AUTHORIZE_PERSONAL_CONTENT": "Pour pouvoir parcourir vos playlistes, favoris et vos musique, vous devez autoriser le plugin", + "AUTHORIZE_PERSONAL_CONTENT_INSTRUCTIONS": "Encore une étape pour activer Spotfy: Cliquez sur 'Autoriser' et suivez les instructions", + "AUTHORIZE": "Autoriser", + "SPOTIFY_LOGIN": "Connexion à Spotify", + "LOGGING_IN": "Déconnexion de Spotify", + "LOGIN_SUCCESSFUL": "Connexion avec succès", + "LOGOUT_SUCCESSFUL": "Déconnexion de Spotify ok", + "SPOTIFY_LOGIN_FAILED": "Echec de la connexion à Spotify", + "SUCCESSFULLY_AUTHORIZED": "Autorisation acceptée", + "CHECK_USERNAME_AND_PASSWORD": "Merci de vérifier votre nom d'utilisateur et mot de passe", + "PROVIDE_USERNAME_AND_PASSWORD": "Merci d'entrer votre nom d'utilisateur et mot de passe", + "MY_PLAYLISTS": "Mes Playlistes", + "MY_ALBUMS": "Mes Albums", + "MY_TRACKS": "Mes pistes", + "MY_TOP_ARTISTS": "Mes Top Artistes", + "MY_TOP_TRACKS": "Mes Top pistes", + "MY_RECENTLY_PLAYED_TRACKS": "Mes pistes jouées récemment", + "FEATURED_PLAYLISTS": "En vedette", + "WHATS_NEW": "Quoi de neuf", + "GENRES_AND_MOODS": "Genres & Ambiances", + "MY_MUSIC": "Ma Musique" } diff --git a/spotify/i18n/strings_it.json b/spotify/i18n/strings_it.json index 71fa7e1e0..31bf57b1a 100755 --- a/spotify/i18n/strings_it.json +++ b/spotify/i18n/strings_it.json @@ -1,43 +1,43 @@ { - "SPOTIFY":"Spotify", - "USERNAME":"Nome Utente", - "PASSWORD":"Password", - "SPOTIFY_USERNAME":"Account Spotify", - "SPOTIFY_PASSWORD":"Password Spotify", - "HIGH_BITRATE":"Alta qualità", - "SEARCH_RESULTS":"Numero di risultati", - "PLUGINS":"Last.fm", - "LAST_FM_USERNAME":"Account Last.fm", - "LAST_FM_PASSWORD":"Password Last.fm", - "SEARCH_SONGS_SECTION":"Canzoni da Spotify", - "SEARCH_ALBUMS_SECTION":"Album da Spotify", - "SEARCH_ARTISTS_SECTION":"Artisti da Spotify", - "SPOTIFY_CONFIGURATION":"Configurazione di Spotify", - "SPOTIFY_ACCOUNT":"Account Spotify", - "SAVE":"Salva", - "LOGIN":"Accedi", - "LOGOUT":"Esci", - "AUTHORIZE_PERSONAL_CONTENT":"Per accedere alla tua musica, le tue playlist e i preferiti, per favore autorizza l'accesso", - "AUTHORIZE_PERSONAL_CONTENT_INSTRUCTIONS":"Per abilitare Spotify è richiesto un ulteriore passaggio: autorizza l'applicazione cliccando sul pulsante Autorizza e esegui le azioni richieste", - "AUTHORIZE":"Autorizza", - "SPOTIFY_LOGIN":"Accesso a Spotify", - "LOGGING_IN":"Accedendo a Spotify", - "LOGIN_SUCCESSFUL":"Accesso a Spotify effettuato con successo", - "LOGOUT_SUCCESSFUL":"Uscita effettuata da Spotify", - "SPOTIFY_LOGIN_FAILED":"Accesso a Spotify fallito", - "SUCCESSFULLY_AUTHORIZED":"Accesso autorizzato con successo", - "CHECK_USERNAME_AND_PASSWORD":"Controlla username e password", - "PROVIDE_USERNAME_AND_PASSWORD":"Fornire username e password", - "MY_PLAYLISTS":"Le mie Playlist", - "MY_ALBUMS":"I miei Album", - "MY_TRACKS":"Le mie tracce", - "MY_TOP_ARTISTS":"I miei migliori Artisti", - "MY_TOP_TRACKS":"Le mie tracce migliori", - "MY_RECENTLY_PLAYED_TRACKS":"Le mie tracce recenti", - "FEATURED_PLAYLISTS":"Playlist in evidenza", - "WHATS_NEW":"Cosa c'è di nuovo", - "GENRES_AND_MOODS":"Generi e Umori", - "MY_MUSIC":"La mia musica", + "SPOTIFY": "Spotify", + "USERNAME": "Nome Utente", + "PASSWORD": "Password", + "SPOTIFY_USERNAME": "Account Spotify", + "SPOTIFY_PASSWORD": "Password Spotify", + "HIGH_BITRATE": "Alta qualità", + "SEARCH_RESULTS": "Numero di risultati", + "PLUGINS": "Last.fm", + "LAST_FM_USERNAME": "Account Last.fm", + "LAST_FM_PASSWORD": "Password Last.fm", + "SEARCH_SONGS_SECTION": "Canzoni da Spotify", + "SEARCH_ALBUMS_SECTION": "Album da Spotify", + "SEARCH_ARTISTS_SECTION": "Artisti da Spotify", + "SPOTIFY_CONFIGURATION": "Configurazione di Spotify", + "SPOTIFY_ACCOUNT": "Account Spotify", + "SAVE": "Salva", + "LOGIN": "Accedi", + "LOGOUT": "Esci", + "AUTHORIZE_PERSONAL_CONTENT": "Per accedere alla tua musica, le tue playlist e i preferiti, per favore autorizza l'accesso", + "AUTHORIZE_PERSONAL_CONTENT_INSTRUCTIONS": "Per abilitare Spotify è richiesto un ulteriore passaggio: autorizza l'applicazione cliccando sul pulsante Autorizza e esegui le azioni richieste", + "AUTHORIZE": "Autorizza", + "SPOTIFY_LOGIN": "Accesso a Spotify", + "LOGGING_IN": "Accedendo a Spotify", + "LOGIN_SUCCESSFUL": "Accesso a Spotify effettuato con successo", + "LOGOUT_SUCCESSFUL": "Uscita effettuata da Spotify", + "SPOTIFY_LOGIN_FAILED": "Accesso a Spotify fallito", + "SUCCESSFULLY_AUTHORIZED": "Accesso autorizzato con successo", + "CHECK_USERNAME_AND_PASSWORD": "Controlla username e password", + "PROVIDE_USERNAME_AND_PASSWORD": "Fornire username e password", + "MY_PLAYLISTS": "Le mie Playlist", + "MY_ALBUMS": "I miei Album", + "MY_TRACKS": "Le mie tracce", + "MY_TOP_ARTISTS": "I miei migliori Artisti", + "MY_TOP_TRACKS": "Le mie tracce migliori", + "MY_RECENTLY_PLAYED_TRACKS": "Le mie tracce recenti", + "FEATURED_PLAYLISTS": "Playlist in evidenza", + "WHATS_NEW": "Cosa c'è di nuovo", + "GENRES_AND_MOODS": "Generi e Umori", + "MY_MUSIC": "La mia musica", "VOLSPOTCONNECTCONF": "Configurazione di Spotify", "VOLSPOTCONNECTACCOUNT": "Account Spotify", "HIGHBITRATE": "Stream bitrate", @@ -57,10 +57,10 @@ "DESCGAPLESS": "Enable/Disable gapless playback between tracks", "AUTOPLAY": "Autoplay", "DESCAUTOPLAY": "Autoplay similar songs when your music ends ", - "OPEN_OR_INSTALL_SPOTIFY_TITLE":"Apri o installa Spotify", - "OPEN_OR_INSTALL_SPOTIFY_DESCRIPTION":"Per ascoltare Spotify, installa o apri Spotify su un qualsiasi dispositivo in questa rete e seleziona questo dispositivo come uscita. E' richiesto un account Spotify Premium", + "OPEN_OR_INSTALL_SPOTIFY_TITLE": "Apri o installa Spotify", + "OPEN_OR_INSTALL_SPOTIFY_DESCRIPTION": "Per ascoltare Spotify, installa o apri Spotify su un qualsiasi dispositivo in questa rete e seleziona questo dispositivo come uscita. E' richiesto un account Spotify Premium", "LOGIN_TO_ENABLE_BROWSING": "Per accedere alle tue tracce e sfogliare la tua libreria, effettua l'accesso a Spotify", "SPOTIFY_BROWSING": "Sfoglia Spotify", "LOGGED_IN_AS": "Accesso effettuato come", "CONFIGURATION_SUCCESSFULLY_UPDATED": "La configurazione è stata aggiornata" -} \ No newline at end of file +} diff --git a/spotify/i18n/strings_nl.json b/spotify/i18n/strings_nl.json index 25a28d6c2..beaa0faf1 100755 --- a/spotify/i18n/strings_nl.json +++ b/spotify/i18n/strings_nl.json @@ -1,45 +1,45 @@ { - "SPOTIFY":"Spotify", - "USERNAME":"Gebruikersnaam", - "PASSWORD":"Wachtwoord", - "SPOTIFY_USERNAME":"Spotify gebruikersnaam", - "SPOTIFY_PASSWORD":"Spotify wachtwoord", - "HIGH_BITRATE":"Hoge kwaliteit", - "REFRESH_TOKEN":"Refresh Token", - "SEARCH_RESULTS":"Aantal resultaten", - "PLUGINS":"Last.fm", - "LAST_FM_USERNAME":"Last.fm gebruikersnaam", - "LAST_FM_PASSWORD":"Last.fm wachtwoord", - "SEARCH_SONGS_SECTION":"Spotify nummers", - "SEARCH_ALBUMS_SECTION":"Spotity albums", - "SEARCH_ARTISTS_SECTION":"Spotify artiesten", - "SPOTIFY_CONFIGURATION":"Spotify configuratie", - "SPOTIFY_ACCOUNT":"Spotify account", - "SAVE":"Opslaan", - "LOGIN":"Aanmelden", - "LOGOUT":"Afmelden", - "AUTHORIZE_PERSONAL_CONTENT":"Geef de applicatie toestemming om je muziek, afspeellijsten en favorieten te bekijken.", - "AUTHORIZE_PERSONAL_CONTENT_INSTRUCTIONS":"Er is nog één stap nodig om Spotfy in te schakelen: autoriseer de applicatie door op de knop Autoriseren te klikken en volg de vereiste stappen", - "AUTHORIZE":"Autoriseren", - "SPOTIFY_LOGIN":"Spotify login", - "LOGGING_IN":"Aanmelden bij Spotify", - "LOGIN_SUCCESSFUL":"Aanmelden bij Spotify succesvol", - "LOGOUT_SUCCESSFUL":"Afgemeld bij Spotify", - "SPOTIFY_LOGIN_FAILED":"Aanmelden bij Spotify mislukt", - "SUCCESSFULLY_AUTHORIZED":"Authoriseren gelukt", - "CHECK_USERNAME_AND_PASSWORD":"Controleer je gebruikersnaam en wachtwoord", - "PROVIDE_USERNAME_AND_PASSWORD":"Geef gebruikersnaam en wachtwoord op", - "MY_PLAYLISTS":"Mijn afspeellijsten", - "MY_ALBUMS":"Mijn albums", - "MY_TRACKS":"Mijn nummers", + "SPOTIFY": "Spotify", + "USERNAME": "Gebruikersnaam", + "PASSWORD": "Wachtwoord", + "SPOTIFY_USERNAME": "Spotify gebruikersnaam", + "SPOTIFY_PASSWORD": "Spotify wachtwoord", + "HIGH_BITRATE": "Hoge kwaliteit", + "REFRESH_TOKEN": "Refresh Token", + "SEARCH_RESULTS": "Aantal resultaten", + "PLUGINS": "Last.fm", + "LAST_FM_USERNAME": "Last.fm gebruikersnaam", + "LAST_FM_PASSWORD": "Last.fm wachtwoord", + "SEARCH_SONGS_SECTION": "Spotify nummers", + "SEARCH_ALBUMS_SECTION": "Spotity albums", + "SEARCH_ARTISTS_SECTION": "Spotify artiesten", + "SPOTIFY_CONFIGURATION": "Spotify configuratie", + "SPOTIFY_ACCOUNT": "Spotify account", + "SAVE": "Opslaan", + "LOGIN": "Aanmelden", + "LOGOUT": "Afmelden", + "AUTHORIZE_PERSONAL_CONTENT": "Geef de applicatie toestemming om je muziek, afspeellijsten en favorieten te bekijken.", + "AUTHORIZE_PERSONAL_CONTENT_INSTRUCTIONS": "Er is nog één stap nodig om Spotfy in te schakelen: autoriseer de applicatie door op de knop Autoriseren te klikken en volg de vereiste stappen", + "AUTHORIZE": "Autoriseren", + "SPOTIFY_LOGIN": "Spotify login", + "LOGGING_IN": "Aanmelden bij Spotify", + "LOGIN_SUCCESSFUL": "Aanmelden bij Spotify succesvol", + "LOGOUT_SUCCESSFUL": "Afgemeld bij Spotify", + "SPOTIFY_LOGIN_FAILED": "Aanmelden bij Spotify mislukt", + "SUCCESSFULLY_AUTHORIZED": "Authoriseren gelukt", + "CHECK_USERNAME_AND_PASSWORD": "Controleer je gebruikersnaam en wachtwoord", + "PROVIDE_USERNAME_AND_PASSWORD": "Geef gebruikersnaam en wachtwoord op", + "MY_PLAYLISTS": "Mijn afspeellijsten", + "MY_ALBUMS": "Mijn albums", + "MY_TRACKS": "Mijn nummers", "MY_ARTISTS": "Mijn artiesten", - "MY_TOP_ARTISTS":"Mijn top artiesten", - "MY_TOP_TRACKS":"Mijn top nummers", - "MY_RECENTLY_PLAYED_TRACKS":"Mijn recent afgespeelde nummers", - "FEATURED_PLAYLISTS":"Aanbevolen afspeellijsten", - "WHATS_NEW":"Wat is er nieuw", - "GENRES_AND_MOODS":"Genres & stemmingen", - "MY_MUSIC":"Mijn muziek", + "MY_TOP_ARTISTS": "Mijn top artiesten", + "MY_TOP_TRACKS": "Mijn top nummers", + "MY_RECENTLY_PLAYED_TRACKS": "Mijn recent afgespeelde nummers", + "FEATURED_PLAYLISTS": "Aanbevolen afspeellijsten", + "WHATS_NEW": "Wat is er nieuw", + "GENRES_AND_MOODS": "Genres & stemmingen", + "MY_MUSIC": "Mijn muziek", "VOLSPOTCONNECTCONF": "Spotify Configuratie", "VOLSPOTCONNECTACCOUNT": "Spotify account", "HIGHBITRATE": "Stream bitrate", @@ -59,12 +59,12 @@ "DESCGAPLESS": "Schakel het afspelen zonder onderbrekingen tussen tracks in/uit", "AUTOPLAY": "Automatisch afspelen", "DESCAUTOPLAY": "Speel vergelijkbare nummers automatisch af wanneer je muziek eindigt", - "OPEN_OR_INSTALL_SPOTIFY_TITLE":"Open of installeer Spotify", - "OPEN_OR_INSTALL_SPOTIFY_DESCRIPTION":"Om naar Spotify te luisteren, opent of installeert u eenvoudig Spotify vanaf elk apparaat in het netwerk en selecteert u dit apparaat als uitvoer. Spotify Premium is vereist", + "OPEN_OR_INSTALL_SPOTIFY_TITLE": "Open of installeer Spotify", + "OPEN_OR_INSTALL_SPOTIFY_DESCRIPTION": "Om naar Spotify te luisteren, opent of installeert u eenvoudig Spotify vanaf elk apparaat in het netwerk en selecteert u dit apparaat als uitvoer. Spotify Premium is vereist", "LOGIN_TO_ENABLE_BROWSING": "Om door uw nummers, albums en afspeellijsten te bladeren, logt u in met uw Spotify-gegevens", "SPOTIFY_BROWSING": "Spotify Browsen", "LOGGED_IN_AS": "Aangemeld als", "CONFIGURATION_SUCCESSFULLY_UPDATED": "Configuratie is succesvol bijgewerkt", "CONNECT_DEVICE_ICON_DOC": "Kies welk pictogram het apparaat in uw Spotify-app in Connect-modus laat zien", - "CONNECT_DEVICE_ICON":"Apparaatpictogram" + "CONNECT_DEVICE_ICON": "Apparaatpictogram" } diff --git a/spotify/index.js b/spotify/index.js index 5f7ed5862..588af144e 100644 --- a/spotify/index.js +++ b/spotify/index.js @@ -41,7 +41,6 @@ var ignoreStopEvent = false; var deltaVolumeTreshold = 2; var volumeDebounce; - // Debug var isDebugMode = true; @@ -49,2881 +48,2848 @@ var isDebugMode = true; module.exports = ControllerSpotify; function ControllerSpotify(context) { - // This fixed variable will let us refer to 'this' object at deeper scopes - var self = this; - - this.context = context; - this.commandRouter = this.context.coreCommand; - this.logger = this.context.logger; - this.configManager = this.context.configManager; - this.resetSpotifyState(); + // This fixed variable will let us refer to 'this' object at deeper scopes + var self = this; + + this.context = context; + this.commandRouter = this.context.coreCommand; + this.logger = this.context.logger; + this.configManager = this.context.configManager; + this.resetSpotifyState(); } - ControllerSpotify.prototype.onVolumioStart = function () { - var self = this; - var configFile = this.commandRouter.pluginManager.getConfigurationFile(this.context, 'config.json'); - this.config = new (require('v-conf'))(); - this.config.loadFile(configFile); + var self = this; + var configFile = this.commandRouter.pluginManager.getConfigurationFile(this.context, 'config.json'); + this.config = new (require('v-conf'))(); + this.config.loadFile(configFile); - return libQ.resolve(); -} + return libQ.resolve(); +}; ControllerSpotify.prototype.getConfigurationFiles = function () { - return ['config.json']; -} + return ['config.json']; +}; ControllerSpotify.prototype.onStop = function () { - var self = this; - var defer = libQ.defer(); + var self = this; + var defer = libQ.defer(); - self.goLibrespotDaemonWsConnection('stop'); - self.stopLibrespotDaemon(); - self.stopSocketStateListener(); - self.removeToBrowseSources(); + self.goLibrespotDaemonWsConnection('stop'); + self.stopLibrespotDaemon(); + self.stopSocketStateListener(); + self.removeToBrowseSources(); - defer.resolve(); - return defer.promise; + defer.resolve(); + return defer.promise; }; ControllerSpotify.prototype.onStart = function () { - var self = this; - var defer = libQ.defer(); + var self = this; + var defer = libQ.defer(); - self.loadI18n(); - self.browseCache = new NodeCache({ stdTTL: 3600, checkperiod: 120 }); - self.initializeLibrespotDaemon(); - self.initializeSpotifyBrowsingFacility(); - defer.resolve(); - return defer.promise; + self.loadI18n(); + self.browseCache = new NodeCache({ stdTTL: 3600, checkperiod: 120 }); + self.initializeLibrespotDaemon(); + self.initializeSpotifyBrowsingFacility(); + defer.resolve(); + return defer.promise; }; - ControllerSpotify.prototype.getUIConfig = function () { - var defer = libQ.defer(); - var self = this; - - var lang_code = self.commandRouter.sharedVars.get('language_code'); - - self.commandRouter.i18nJson(__dirname + '/i18n/strings_' + lang_code + '.json', - __dirname + '/i18n/strings_en.json', - __dirname + '/UIConfig.json') - .then(function (uiconf) { - var credentials_type = self.config.get('credentials_type', 'zeroconf'); - if (self.loggedInUserId !== undefined && credentials_type === 'spotify_token') { - uiconf.sections[1].content[0].hidden = true; - uiconf.sections[1].content[1].hidden = false; - } - var bitrateNumber = self.config.get('bitrate_number', 320); - uiconf.sections[2].content[0].value.value = bitrateNumber - uiconf.sections[2].content[0].value.label = self.getLabelForSelect(uiconf.sections[2].content[0].options, bitrateNumber); - - var normalisationPregainValue = self.config.get('normalisation_pregain', '1.0'); - uiconf.sections[2].content[2].value.value = normalisationPregainValue; - uiconf.sections[2].content[2].value.label = normalisationPregainValue; - - var icon = self.config.get('icon', 'avr'); - uiconf.sections[2].content[3].value.value = icon; - uiconf.sections[2].content[3].value.label = self.getLabelForSelect(uiconf.sections[2].content[3].options, icon); - - defer.resolve(uiconf); - }) - .fail(function (error) { - self.logger.error('Cannot populate Spotify configuration: ' + error); - defer.reject(new Error()); - }); + var defer = libQ.defer(); + var self = this; + + var lang_code = self.commandRouter.sharedVars.get('language_code'); + + self.commandRouter + .i18nJson( + __dirname + '/i18n/strings_' + lang_code + '.json', + __dirname + '/i18n/strings_en.json', + __dirname + '/UIConfig.json', + ) + .then(function (uiconf) { + var credentials_type = self.config.get('credentials_type', 'zeroconf'); + if (self.loggedInUserId !== undefined && credentials_type === 'spotify_token') { + uiconf.sections[1].content[0].hidden = true; + uiconf.sections[1].content[1].hidden = false; + } + var bitrateNumber = self.config.get('bitrate_number', 320); + uiconf.sections[2].content[0].value.value = bitrateNumber; + uiconf.sections[2].content[0].value.label = self.getLabelForSelect( + uiconf.sections[2].content[0].options, + bitrateNumber, + ); + + var normalisationPregainValue = self.config.get('normalisation_pregain', '1.0'); + uiconf.sections[2].content[2].value.value = normalisationPregainValue; + uiconf.sections[2].content[2].value.label = normalisationPregainValue; + + var icon = self.config.get('icon', 'avr'); + uiconf.sections[2].content[3].value.value = icon; + uiconf.sections[2].content[3].value.label = self.getLabelForSelect(uiconf.sections[2].content[3].options, icon); + + defer.resolve(uiconf); + }) + .fail(function (error) { + self.logger.error('Cannot populate Spotify configuration: ' + error); + defer.reject(new Error()); + }); - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.getAdditionalConf = function (type, controller, data, def) { - var self = this; - var setting = self.commandRouter.executeOnPlugin(type, controller, 'getConfigParam', data); + var self = this; + var setting = self.commandRouter.executeOnPlugin(type, controller, 'getConfigParam', data); - if (setting == undefined) { - setting = def; - } - return setting; + if (setting == undefined) { + setting = def; + } + return setting; }; // Controls ControllerSpotify.prototype.goLibrespotDaemonWsConnection = function (action) { - var self = this; - - if (action === 'start') { - wsConnectionStatus = 'started'; + var self = this; + + if (action === 'start') { + wsConnectionStatus = 'started'; + self.initializeWsConnection(); + } else if (action === 'stop') { + if (ws) { + ws.terminate(); + ws = undefined; + } + wsConnectionStatus = 'stopped'; + } else if (action === 'restart') { + if (wsConnectionStatus === 'started') { + if (restartTimeout) { + clearTimeout(restartTimeout); + } + restartTimeout = setTimeout(() => { self.initializeWsConnection(); - } else if (action === 'stop') { - if (ws) { - ws.terminate(); - ws = undefined; - } - wsConnectionStatus = 'stopped'; - } else if (action === 'restart'){ - if (wsConnectionStatus === 'started') { - if (restartTimeout) { - clearTimeout(restartTimeout); - } - restartTimeout = setTimeout(()=>{ - self.initializeWsConnection(); - restartTimeout = undefined; - }, 3000); - } + restartTimeout = undefined; + }, 3000); } + } }; ControllerSpotify.prototype.initializeWsConnection = function () { - var self = this; + var self = this; - self.logger.info('Initializing connection to go-librespot Websocket'); + self.logger.info('Initializing connection to go-librespot Websocket'); - ws = new websocket('ws://localhost:' + spotifyDaemonPort + '/events'); - ws.on('error', function(error){ - self.logger.info('Error connecting to go-librespot Websocket: ' + error); - self.goLibrespotDaemonWsConnection('restart'); - }); + ws = new websocket('ws://localhost:' + spotifyDaemonPort + '/events'); + ws.on('error', function (error) { + self.logger.info('Error connecting to go-librespot Websocket: ' + error); + self.goLibrespotDaemonWsConnection('restart'); + }); - ws.on('message', function message(data) { - self.debugLog('received: ' + data); - self.parseEventState(JSON.parse(data)); - }); + ws.on('message', function message(data) { + self.debugLog('received: ' + data); + self.parseEventState(JSON.parse(data)); + }); - ws.on('open', function () { - self.logger.info('Connection to go-librespot Websocket established'); - setTimeout(()=>{ - self.initializeSpotifyControls(); - }, 3000); - ws.on('close', function(){ - self.logger.info('Connection to go-librespot Websocket closed'); - self.goLibrespotDaemonWsConnection('restart'); - }); + ws.on('open', function () { + self.logger.info('Connection to go-librespot Websocket established'); + setTimeout(() => { + self.initializeSpotifyControls(); + }, 3000); + ws.on('close', function () { + self.logger.info('Connection to go-librespot Websocket closed'); + self.goLibrespotDaemonWsConnection('restart'); }); + }); }; ControllerSpotify.prototype.initializeSpotifyControls = function () { - var self = this; + var self = this; - self.resetSpotifyState(); - self.startSocketStateListener(); - self.getSpotifyVolume(); + self.resetSpotifyState(); + self.startSocketStateListener(); + self.getSpotifyVolume(); }; ControllerSpotify.prototype.resetSpotifyState = function () { - var self = this; - - this.state = { - status: 'stop', - service: 'spop', - title: '', - artist: '', - album: '', - albumart: '/albumart', - uri: '', - // icon: 'fa fa-spotify', - trackType: 'spotify', - seek: 0, - duration: 0, - samplerate: '44.1 KHz', - bitdepth: '16 bit', - bitrate: '', - codec: 'ogg', - channels: 2, - random: null, - repeat: null, - repeatSingle: null, - }; + var self = this; + + this.state = { + status: 'stop', + service: 'spop', + title: '', + artist: '', + album: '', + albumart: '/albumart', + uri: '', + // icon: 'fa fa-spotify', + trackType: 'spotify', + seek: 0, + duration: 0, + samplerate: '44.1 KHz', + bitdepth: '16 bit', + bitrate: '', + codec: 'ogg', + channels: 2, + random: null, + repeat: null, + repeatSingle: null, + }; }; ControllerSpotify.prototype.parseEventState = function (event) { - var self = this; - - var pushStateforEvent = false; - - // create a switch case which handles types of events - // and updates the state accordingly - switch (event.type) { - case 'metadata': - self.state.title = event.data.name; - self.state.duration = self.parseDuration(event.data.duration); - self.state.uri = event.data.uri; - self.state.artist = self.parseArtists(event.data.artist_names); - self.state.album = event.data.album_name; - self.state.albumart = event.data.album_cover_url; - self.state.seek = event.data.position; - pushStateforEvent = false; - break; - case 'will_play': - //impro: use this event to free up audio device when starting volatile? - pushStateforEvent = false; - break; - case 'playing': - self.state.status = 'play'; - self.identifyPlaybackMode(event.data); - setTimeout(()=>{ - self.pushState(); - }, 300); - pushStateforEvent = true; - break; - case 'paused': - self.state.status = 'pause'; - self.identifyPlaybackMode(event.data); - pushStateforEvent = true; - break; - case 'stopped': - self.state.status = 'stop'; - pushStateforEvent = true; - break; - case 'seek': - self.state.seek = event.data.position; - pushStateforEvent = true; + var self = this; + + var pushStateforEvent = false; + + // create a switch case which handles types of events + // and updates the state accordingly + switch (event.type) { + case 'metadata': + self.state.title = event.data.name; + self.state.duration = self.parseDuration(event.data.duration); + self.state.uri = event.data.uri; + self.state.artist = self.parseArtists(event.data.artist_names); + self.state.album = event.data.album_name; + self.state.albumart = event.data.album_cover_url; + self.state.seek = event.data.position; + pushStateforEvent = false; + break; + case 'will_play': + //impro: use this event to free up audio device when starting volatile? + pushStateforEvent = false; + break; + case 'playing': + self.state.status = 'play'; + self.identifyPlaybackMode(event.data); + setTimeout(() => { + self.pushState(); + }, 300); + pushStateforEvent = true; + break; + case 'paused': + self.state.status = 'pause'; + self.identifyPlaybackMode(event.data); + pushStateforEvent = true; + break; + case 'stopped': + self.state.status = 'stop'; + pushStateforEvent = true; + break; + case 'seek': + self.state.seek = event.data.position; + pushStateforEvent = true; + break; + case 'active': + //self.state.status = 'play'; + pushStateforEvent = false; + self.alignSpotifyVolumeToVolumioVolume(); + case 'volume': + try { + if (event.data && event.data.value !== undefined) { + self.onSpotifyVolumeChange(parseInt(event.data.value)); + } + } catch (e) { + self.logger.error('Failed to parse Spotify volume event: ' + e); + } + pushStateforEvent = false; + break; + case 'shuffle_context': + self.state.random = event.data.value; + pushStateforEvent = true; + break; + case 'repeat_context': + self.state.repeatSingle = false; + self.state.repeat = event.data.value; + pushStateforEvent = true; + break; + case 'repeat_track': + if (!event.data.value) { break; - case 'active': - //self.state.status = 'play'; - pushStateforEvent = false; - self.alignSpotifyVolumeToVolumioVolume(); - case 'volume': - try { - if (event.data && event.data.value !== undefined) { - self.onSpotifyVolumeChange(parseInt(event.data.value)); - } - } catch(e) { - self.logger.error('Failed to parse Spotify volume event: ' + e); - } - pushStateforEvent = false; - break; - case 'shuffle_context': - self.state.random = event.data.value; - pushStateforEvent = true; - break; - case 'repeat_context': - self.state.repeatSingle = false; - self.state.repeat = event.data.value; - pushStateforEvent = true; - break; - case 'repeat_track': - if (!event.data.value) { - break; - } - self.state.repeatSingle = true; - self.state.repeat = true; - pushStateforEvent = true; - break; - default: - self.logger.error('Failed to decode event: ' + event.type); - pushStateforEvent = false; - break; - } - - if (pushStateforEvent) { - self.pushState(self.state); - } + } + self.state.repeatSingle = true; + self.state.repeat = true; + pushStateforEvent = true; + break; + default: + self.logger.error('Failed to decode event: ' + event.type); + pushStateforEvent = false; + break; + } + + if (pushStateforEvent) { + self.pushState(self.state); + } }; ControllerSpotify.prototype.identifyPlaybackMode = function (data) { - var self = this; - - // This functions checks if Spotify is playing in volatile mode or in Volumio mode (playback started from Volumio UI) - // play_origin = 'go-librespot' means that Spotify is playing in Volumio mode - // play_origin = 'your_library' or 'playlist' means that Spotify is playing in volatile mode - if (data && data.play_origin && data.play_origin === 'go-librespot') { - isInVolatileMode = false; - } else { - isInVolatileMode = true; - } - - // Refactor in order to handle the case where current service is spop but not in volatile mode - if ((isInVolatileMode && currentVolumioState.service !== 'spop') || - (isInVolatileMode && currentVolumioState.service === 'spop' && currentVolumioState.volatile !== true)) { - self.initializeSpotifyPlaybackInVolatileMode(); - } - + var self = this; + + // This functions checks if Spotify is playing in volatile mode or in Volumio mode (playback started from Volumio UI) + // play_origin = 'go-librespot' means that Spotify is playing in Volumio mode + // play_origin = 'your_library' or 'playlist' means that Spotify is playing in volatile mode + if (data && data.play_origin && data.play_origin === 'go-librespot') { + isInVolatileMode = false; + } else { + isInVolatileMode = true; + } + + // Refactor in order to handle the case where current service is spop but not in volatile mode + if ( + (isInVolatileMode && currentVolumioState.service !== 'spop') || + (isInVolatileMode && currentVolumioState.service === 'spop' && currentVolumioState.volatile !== true) + ) { + self.initializeSpotifyPlaybackInVolatileMode(); + } }; ControllerSpotify.prototype.initializeSpotifyPlaybackInVolatileMode = function () { - var self = this; + var self = this; - self.logger.info('Spotify is playing in volatile mode'); - ignoreStopEvent = true; + self.logger.info('Spotify is playing in volatile mode'); + ignoreStopEvent = true; - self.commandRouter.stateMachine.setConsumeUpdateService(undefined); - self.context.coreCommand.stateMachine.setVolatile({ - service: 'spop', - callback: self.libRespotGoUnsetVolatile() - }); + self.commandRouter.stateMachine.setConsumeUpdateService(undefined); + self.context.coreCommand.stateMachine.setVolatile({ + service: 'spop', + callback: self.libRespotGoUnsetVolatile(), + }); - setTimeout(()=>{ - ignoreStopEvent = false; - }, 2000); + setTimeout(() => { + ignoreStopEvent = false; + }, 2000); }; ControllerSpotify.prototype.parseDuration = function (spotifyDuration) { - var self = this; + var self = this; - try { - return parseInt(spotifyDuration/1000); - } catch(e) { - return 0; - } -} + try { + return parseInt(spotifyDuration / 1000); + } catch (e) { + return 0; + } +}; ControllerSpotify.prototype.getCurrentBitrate = function () { - var self = this; + var self = this; - return self.selectedBitrate + ' kbps'; -} + return self.selectedBitrate + ' kbps'; +}; ControllerSpotify.prototype.parseArtists = function (spotifyArtists) { - var self = this; - - var artist = ''; - if (spotifyArtists.length > 0) { - for (var i in spotifyArtists) { - if (!artist.length) { - artist = spotifyArtists[i]; - } else { - artist = artist + ', ' + spotifyArtists[i]; - } - } - return artist; - } else { - return spotifyArtists; + var self = this; + + var artist = ''; + if (spotifyArtists.length > 0) { + for (var i in spotifyArtists) { + if (!artist.length) { + artist = spotifyArtists[i]; + } else { + artist = artist + ', ' + spotifyArtists[i]; + } } -} - + return artist; + } else { + return spotifyArtists; + } +}; ControllerSpotify.prototype.libRespotGoUnsetVolatile = function () { - var self = this; - var defer = libQ.defer(); - - self.debugLog('UNSET VOLATILE'); - self.debugLog(JSON.stringify(currentVolumioState)) - - if (currentVolumioState && currentVolumioState.status && currentVolumioState.status !== 'stop') { - self.logger.info('Setting Spotify stop after unset volatile call'); - setTimeout(()=>{ - self.stop(); - defer.resolve(''); - }, 500); - } else { - defer.resolve(''); - } -} + var self = this; + var defer = libQ.defer(); + + self.debugLog('UNSET VOLATILE'); + self.debugLog(JSON.stringify(currentVolumioState)); + + if (currentVolumioState && currentVolumioState.status && currentVolumioState.status !== 'stop') { + self.logger.info('Setting Spotify stop after unset volatile call'); + setTimeout(() => { + self.stop(); + defer.resolve(''); + }, 500); + } else { + defer.resolve(''); + } +}; ControllerSpotify.prototype.getState = function () { - var self = this; + var self = this; - self.debugLog('GET STATE SPOTIFY'); - self.debugLog(JSON.stringify(self.state)); - return self.state; + self.debugLog('GET STATE SPOTIFY'); + self.debugLog(JSON.stringify(self.state)); + return self.state; }; // Announce updated Spop state ControllerSpotify.prototype.pushState = function (state) { - var self = this; + var self = this; - self.state.bitrate = self.getCurrentBitrate(); - self.debugLog('PUSH STATE SPOTIFY'); - self.debugLog(JSON.stringify(self.state)); - self.seekTimerAction(); - return self.commandRouter.servicePushState(self.state, 'spop'); + self.state.bitrate = self.getCurrentBitrate(); + self.debugLog('PUSH STATE SPOTIFY'); + self.debugLog(JSON.stringify(self.state)); + self.seekTimerAction(); + return self.commandRouter.servicePushState(self.state, 'spop'); }; ControllerSpotify.prototype.sendSpotifyLocalApiCommand = function (commandPath) { - this.logger.info('Sending Spotify command to local API: ' + commandPath); - - superagent.post(spotifyLocalApiEndpointBase + commandPath) - .accept('application/json') - .then((results) => {}) - .catch((error) => { - this.logger.error('Failed to send command to Spotify local API: ' + commandPath + ': ' + error); - }); + this.logger.info('Sending Spotify command to local API: ' + commandPath); + + superagent + .post(spotifyLocalApiEndpointBase + commandPath) + .accept('application/json') + .then((results) => {}) + .catch((error) => { + this.logger.error('Failed to send command to Spotify local API: ' + commandPath + ': ' + error); + }); }; ControllerSpotify.prototype.sendSpotifyLocalApiCommandWithPayload = function (commandPath, payload) { - this.logger.info('Sending Spotify command with payload to local API: ' + commandPath); - - superagent.post(spotifyLocalApiEndpointBase + commandPath) - .accept('application/json') - .send(payload) - .then((results) => {}) - .catch((error) => { - this.logger.error('Failed to send command to Spotify local API: ' + commandPath + ': ' + error); - }); + this.logger.info('Sending Spotify command with payload to local API: ' + commandPath); + + superagent + .post(spotifyLocalApiEndpointBase + commandPath) + .accept('application/json') + .send(payload) + .then((results) => {}) + .catch((error) => { + this.logger.error('Failed to send command to Spotify local API: ' + commandPath + ': ' + error); + }); }; - ControllerSpotify.prototype.pause = function () { - this.logger.info('Spotify Received pause'); + this.logger.info('Spotify Received pause'); - this.debugLog('SPOTIFY PAUSE'); - this.debugLog(JSON.stringify(currentVolumioState)) - this.sendSpotifyLocalApiCommand('/player/pause'); + this.debugLog('SPOTIFY PAUSE'); + this.debugLog(JSON.stringify(currentVolumioState)); + this.sendSpotifyLocalApiCommand('/player/pause'); }; ControllerSpotify.prototype.play = function () { - this.logger.info('Spotify Play'); - - if (this.state.status === 'pause') { - this.sendSpotifyLocalApiCommand('/player/resume'); - } else { - this.sendSpotifyLocalApiCommand('/player/play'); - } + this.logger.info('Spotify Play'); + if (this.state.status === 'pause') { + this.sendSpotifyLocalApiCommand('/player/resume'); + } else { + this.sendSpotifyLocalApiCommand('/player/play'); + } }; ControllerSpotify.prototype.stop = function () { - this.logger.info('Spotify Stop'); - var defer = libQ.defer(); + this.logger.info('Spotify Stop'); + var defer = libQ.defer(); - this.debugLog('SPOTIFY STOP'); - this.debugLog(JSON.stringify(currentVolumioState)) - if (!ignoreStopEvent) { - this.sendSpotifyLocalApiCommand('/player/pause'); - } + this.debugLog('SPOTIFY STOP'); + this.debugLog(JSON.stringify(currentVolumioState)); + if (!ignoreStopEvent) { + this.sendSpotifyLocalApiCommand('/player/pause'); + } - defer.resolve(''); - return defer.promise; + defer.resolve(''); + return defer.promise; }; - ControllerSpotify.prototype.resume = function () { - this.logger.info('Spotify Resume'); + this.logger.info('Spotify Resume'); - this.sendSpotifyLocalApiCommand('/player/resume'); + this.sendSpotifyLocalApiCommand('/player/resume'); }; ControllerSpotify.prototype.next = function () { - this.logger.info('Spotify next'); + this.logger.info('Spotify next'); - this.sendSpotifyLocalApiCommand('/player/next'); + this.sendSpotifyLocalApiCommand('/player/next'); }; ControllerSpotify.prototype.previous = function () { - this.logger.info('Spotify previous'); + this.logger.info('Spotify previous'); - this.sendSpotifyLocalApiCommand('/player/prev'); + this.sendSpotifyLocalApiCommand('/player/prev'); }; ControllerSpotify.prototype.seek = function (position) { - this.logger.info('Spotify seek to: ' + position); + this.logger.info('Spotify seek to: ' + position); - this.sendSpotifyLocalApiCommandWithPayload('/player/seek', { position: position }); + this.sendSpotifyLocalApiCommandWithPayload('/player/seek', { position: position }); }; ControllerSpotify.prototype.random = function (value) { - this.logger.info('Spotify Random: ' + value); - this.sendSpotifyLocalApiCommandWithPayload('/player/shuffle_context', { shuffle_context: value }); + this.logger.info('Spotify Random: ' + value); + this.sendSpotifyLocalApiCommandWithPayload('/player/shuffle_context', { shuffle_context: value }); }; ControllerSpotify.prototype.repeat = function (value, repeatSingle) { - this.logger.info('Spotify Repeat: ' + value + ' - ' + repeatSingle); - if (repeatSingle) { - this.sendSpotifyLocalApiCommandWithPayload('/player/repeat_track', { repeat_track: true }); - } else if (value) { - this.sendSpotifyLocalApiCommandWithPayload('/player/repeat_context', { repeat_context: true }); - } else { - this.sendSpotifyLocalApiCommandWithPayload('/player/repeat_context', { repeat_context: false }); - this.sendSpotifyLocalApiCommandWithPayload('/player/repeat_track', { repeat_track: false }); - } + this.logger.info('Spotify Repeat: ' + value + ' - ' + repeatSingle); + if (repeatSingle) { + this.sendSpotifyLocalApiCommandWithPayload('/player/repeat_track', { repeat_track: true }); + } else if (value) { + this.sendSpotifyLocalApiCommandWithPayload('/player/repeat_context', { repeat_context: true }); + } else { + this.sendSpotifyLocalApiCommandWithPayload('/player/repeat_context', { repeat_context: false }); + this.sendSpotifyLocalApiCommandWithPayload('/player/repeat_track', { repeat_track: false }); + } }; // Volume events ControllerSpotify.prototype.onSpotifyVolumeChange = function (volume) { - var self = this; - - self.debugLog('RECEIVED SPOTIFY VOLUME ' + volume); - if (volume !== currentVolumioVolume) { - self.logger.info('Setting Volumio Volume from Spotify: ' + volume); - currentSpotifyVolume = volume; - currentVolumioVolume = currentSpotifyVolume; - self.commandRouter.volumiosetvolume(currentVolumioVolume); - } + var self = this; + self.debugLog('RECEIVED SPOTIFY VOLUME ' + volume); + if (volume !== currentVolumioVolume) { + self.logger.info('Setting Volumio Volume from Spotify: ' + volume); + currentSpotifyVolume = volume; + currentVolumioVolume = currentSpotifyVolume; + self.commandRouter.volumiosetvolume(currentVolumioVolume); + } }; ControllerSpotify.prototype.onVolumioVolumeChange = function (volume) { - var self = this; - - self.debugLog('RECEIVED VOLUMIO VOLUME ' + volume); - if (volume !== currentSpotifyVolume && self.checkSpotifyAndVolumioDeltaVolumeIsEnough(currentSpotifyVolume, volume)) { - self.logger.info('Setting Spotify Volume from Volumio: ' + volume); - currentVolumioVolume = volume; - currentSpotifyVolume = currentVolumioVolume; - self.setSpotifyDaemonVolume(currentSpotifyVolume); - } + var self = this; + + self.debugLog('RECEIVED VOLUMIO VOLUME ' + volume); + if (volume !== currentSpotifyVolume && self.checkSpotifyAndVolumioDeltaVolumeIsEnough(currentSpotifyVolume, volume)) { + self.logger.info('Setting Spotify Volume from Volumio: ' + volume); + currentVolumioVolume = volume; + currentSpotifyVolume = currentVolumioVolume; + self.setSpotifyDaemonVolume(currentSpotifyVolume); + } }; ControllerSpotify.prototype.setSpotifyDaemonVolume = function (volume) { - var self = this; + var self = this; - // Volume limiter - if (volumeDebounce) { - clearTimeout(volumeDebounce); - } - volumeDebounce = setTimeout(() => { - self.debugLog('SETTING SPOTIFY VOLUME ' + volume); - self.sendSpotifyLocalApiCommandWithPayload('/player/volume', { volume: volume }); - }, 1500); + // Volume limiter + if (volumeDebounce) { + clearTimeout(volumeDebounce); + } + volumeDebounce = setTimeout(() => { + self.debugLog('SETTING SPOTIFY VOLUME ' + volume); + self.sendSpotifyLocalApiCommandWithPayload('/player/volume', { volume: volume }); + }, 1500); }; - ControllerSpotify.prototype.checkSpotifyAndVolumioDeltaVolumeIsEnough = function (spotifyVolume, volumioVolume) { - var self = this; - - self.debugLog('SPOTIFY VOLUME ' + spotifyVolume); - self.debugLog('VOLUMIO VOLUME ' + volumioVolume); - if (spotifyVolume === undefined) { - return self.alignSpotifyVolumeToVolumioVolume(); - } - try { - var isDeltaVolumeEnough = Math.abs(parseInt(spotifyVolume) - parseInt(volumioVolume)) >= deltaVolumeTreshold; - self.debugLog('DELTA VOLUME ENOUGH: ' + isDeltaVolumeEnough); - return isDeltaVolumeEnough; - } catch(e) { - return false; - } + var self = this; + + self.debugLog('SPOTIFY VOLUME ' + spotifyVolume); + self.debugLog('VOLUMIO VOLUME ' + volumioVolume); + if (spotifyVolume === undefined) { + return self.alignSpotifyVolumeToVolumioVolume(); + } + try { + var isDeltaVolumeEnough = Math.abs(parseInt(spotifyVolume) - parseInt(volumioVolume)) >= deltaVolumeTreshold; + self.debugLog('DELTA VOLUME ENOUGH: ' + isDeltaVolumeEnough); + return isDeltaVolumeEnough; + } catch (e) { + return false; + } }; ControllerSpotify.prototype.alignSpotifyVolumeToVolumioVolume = function () { - var self = this; + var self = this; - self.logger.info('Aligning Spotify Volume to Volumio Volume'); + self.logger.info('Aligning Spotify Volume to Volumio Volume'); - let state = self.commandRouter.volumioGetState(); - let currentVolumioVolumeValue = state && state.volume ? state.volume : undefined; - let currentDisableVolumeControl = state && state.disableVolumeControl ? state.disableVolumeControl : undefined; - let currentMuteValue = state && state.mute ? state.mute : undefined; - if (currentVolumioVolumeValue !== undefined && currentDisableVolumeControl !== true) { - if (currentMuteValue === true) { - currentVolumioVolume = 0; - } else { - currentVolumioVolume = currentVolumioVolumeValue; - } - self.logger.info('Setting Spotify Volume from Volumio: ' + currentVolumioVolume); - currentSpotifyVolume = currentVolumioVolume; - self.setSpotifyDaemonVolume(currentSpotifyVolume); + let state = self.commandRouter.volumioGetState(); + let currentVolumioVolumeValue = state && state.volume ? state.volume : undefined; + let currentDisableVolumeControl = state && state.disableVolumeControl ? state.disableVolumeControl : undefined; + let currentMuteValue = state && state.mute ? state.mute : undefined; + if (currentVolumioVolumeValue !== undefined && currentDisableVolumeControl !== true) { + if (currentMuteValue === true) { + currentVolumioVolume = 0; + } else { + currentVolumioVolume = currentVolumioVolumeValue; } + self.logger.info('Setting Spotify Volume from Volumio: ' + currentVolumioVolume); + currentSpotifyVolume = currentVolumioVolume; + self.setSpotifyDaemonVolume(currentSpotifyVolume); + } }; - ControllerSpotify.prototype.clearAddPlayTrack = function (track) { - var self = this; - self.commandRouter.pushConsoleMessage('[' + Date.now() + '] ' + 'ControllerSpotify::clearAddPlayTrack'); - self.resetSpotifyState(); + var self = this; + self.commandRouter.pushConsoleMessage('[' + Date.now() + '] ' + 'ControllerSpotify::clearAddPlayTrack'); + self.resetSpotifyState(); - return self.sendSpotifyLocalApiCommandWithPayload('/player/play', { uri: track.uri }); + return self.sendSpotifyLocalApiCommandWithPayload('/player/play', { uri: track.uri }); }; - ControllerSpotify.prototype.startSocketStateListener = function () { - var self = this; - - if (self.stateSocket) { - self.stateSocket.off(); - self.stateSocket.disconnect(); + var self = this; + + if (self.stateSocket) { + self.stateSocket.off(); + self.stateSocket.disconnect(); + } + + self.stateSocket = io.connect('http://localhost:3000'); + self.stateSocket.on('connect', function () { + self.stateSocket.emit('getState', ''); + }); + + self.stateSocket.on('pushState', function (data) { + currentVolumioState = data; + if (data && data.volume && !data.disableVolumeControl) { + var currentVolume = data.volume; + if (data.mute === true) { + currentVolume = 0; + } + self.onVolumioVolumeChange(currentVolume); } - - self.stateSocket= io.connect('http://localhost:3000'); - self.stateSocket.on('connect', function() { - self.stateSocket.emit('getState', ''); - }); - - self.stateSocket.on('pushState', function (data) { - currentVolumioState = data; - if (data && data.volume && !data.disableVolumeControl) { - var currentVolume = data.volume; - if (data.mute === true) { - currentVolume = 0; - } - self.onVolumioVolumeChange(currentVolume); - } - }); + }); }; ControllerSpotify.prototype.stopSocketStateListener = function () { - var self = this; + var self = this; - if (self.stateSocket) { - self.stateSocket.off(); - self.stateSocket.disconnect(); - } + if (self.stateSocket) { + self.stateSocket.off(); + self.stateSocket.disconnect(); + } }; - // DAEMON MANAGEMENT ControllerSpotify.prototype.initializeLibrespotDaemon = function () { - var self = this; - var defer = libQ.defer(); + var self = this; + var defer = libQ.defer(); - this.selectedBitrate = self.config.get('bitrate_number', '320').toString(); + this.selectedBitrate = self.config.get('bitrate_number', '320').toString(); - self.createConfigFile() - .then(function() { - return self.startLibrespotDaemon(); - }) - .then(function() { - self.logger.info('go-librespot daemon successfully initialized'); - setTimeout(()=>{ - self.goLibrespotDaemonWsConnection('start'); - defer.resolve(''); - }, 3000); - }) - .fail(function (e) { - defer.reject(e); - self.logger.error('Error initializing go-librespot daemon: ' + e); - }); + self + .createConfigFile() + .then(function () { + return self.startLibrespotDaemon(); + }) + .then(function () { + self.logger.info('go-librespot daemon successfully initialized'); + setTimeout(() => { + self.goLibrespotDaemonWsConnection('start'); + defer.resolve(''); + }, 3000); + }) + .fail(function (e) { + defer.reject(e); + self.logger.error('Error initializing go-librespot daemon: ' + e); + }); - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.startLibrespotDaemon = function () { - var self = this; - var defer = libQ.defer(); - - exec("/usr/bin/sudo systemctl restart go-librespot-daemon.service", function (error, stdout, stderr) { - if (error) { - self.logger.error('Cannot start Go-librespot Daemon: ' + error); - defer.reject(error); - } else { - setTimeout(()=>{ - defer.resolve(); - }, 3000); - } - }); + var self = this; + var defer = libQ.defer(); - return defer.promise; + exec('/usr/bin/sudo systemctl restart go-librespot-daemon.service', function (error, stdout, stderr) { + if (error) { + self.logger.error('Cannot start Go-librespot Daemon: ' + error); + defer.reject(error); + } else { + setTimeout(() => { + defer.resolve(); + }, 3000); + } + }); + return defer.promise; }; ControllerSpotify.prototype.stopLibrespotDaemon = function () { - var self = this; - var defer = libQ.defer(); + var self = this; + var defer = libQ.defer(); - exec("/usr/bin/sudo systemctl stop go-librespot-daemon.service", function (error, stdout, stderr) { - if (error) { - self.logger.error('Cannot stop Go-librespot Daemon: ' + error); - defer.reject(error); - } else { - setTimeout(() => { - defer.resolve(); - }, 2000); - } - }); + exec('/usr/bin/sudo systemctl stop go-librespot-daemon.service', function (error, stdout, stderr) { + if (error) { + self.logger.error('Cannot stop Go-librespot Daemon: ' + error); + defer.reject(error); + } else { + setTimeout(() => { + defer.resolve(); + }, 2000); + } + }); - return defer.promise; + return defer.promise; }; - ControllerSpotify.prototype.createConfigFile = function () { - var self = this; - var defer = libQ.defer(); - - this.logger.info('Creating Spotify config file'); - - try { - var template = fs.readFileSync(path.join(__dirname, 'config.yml.tmpl'), {encoding: 'utf8'}); - } catch (e) { - this.logger.error('Failed to read template file: ' + e); - } - - var devicename = this.commandRouter.sharedVars.get('system.name'); - var selectedBitrate = self.config.get('bitrate_number', '320').toString(); - var icon = self.config.get('icon', 'avr'); - var externalVolume = true; - var mixerType = self.getAdditionalConf('audio_interface', 'alsa_controller', 'mixer_type', 'None'); - if (mixerType === 'None') { - externalVolume = false; - } - var normalisationPregain = self.config.get('normalisation_pregain', '1.0'); - - var conf = template.replace('${device_name}', devicename) - .replace('${bitrate_number}', selectedBitrate) - .replace('${device_type}', icon) - .replace('${external_volume}', externalVolume) - .replace('${normalisation_pregain}', normalisationPregain); - - var credentials_type = self.config.get('credentials_type', 'zeroconf'); - var logged_user_id = self.config.get('logged_user_id', ''); - var access_token = self.config.get('access_token', ''); - - if (credentials_type === 'spotify_token' && logged_user_id !== '' && access_token !== '') { - conf += 'credentials: ' + os.EOL; - conf += ' type: spotify_token' + os.EOL; - conf += ' spotify_token:' + os.EOL; - conf += ' username: "' + logged_user_id + '"' + os.EOL; - conf += ' access_token: "' + access_token + '"'; + var self = this; + var defer = libQ.defer(); + + this.logger.info('Creating Spotify config file'); + + try { + var template = fs.readFileSync(path.join(__dirname, 'config.yml.tmpl'), { encoding: 'utf8' }); + } catch (e) { + this.logger.error('Failed to read template file: ' + e); + } + + var devicename = this.commandRouter.sharedVars.get('system.name'); + var selectedBitrate = self.config.get('bitrate_number', '320').toString(); + var icon = self.config.get('icon', 'avr'); + var externalVolume = true; + var mixerType = self.getAdditionalConf('audio_interface', 'alsa_controller', 'mixer_type', 'None'); + if (mixerType === 'None') { + externalVolume = false; + } + var normalisationPregain = self.config.get('normalisation_pregain', '1.0'); + + var conf = template + .replace('${device_name}', devicename) + .replace('${bitrate_number}', selectedBitrate) + .replace('${device_type}', icon) + .replace('${external_volume}', externalVolume) + .replace('${normalisation_pregain}', normalisationPregain); + + var credentials_type = self.config.get('credentials_type', 'zeroconf'); + var logged_user_id = self.config.get('logged_user_id', ''); + var access_token = self.config.get('access_token', ''); + + if (credentials_type === 'spotify_token' && logged_user_id !== '' && access_token !== '') { + conf += 'credentials: ' + os.EOL; + conf += ' type: spotify_token' + os.EOL; + conf += ' spotify_token:' + os.EOL; + conf += ' username: "' + logged_user_id + '"' + os.EOL; + conf += ' access_token: "' + access_token + '"'; + } else { + conf += 'credentials: ' + os.EOL; + conf += ' type: zeroconf' + os.EOL; + } + + fs.writeFile(configFileDestinationPath, conf, (err) => { + if (err) { + defer.reject(err); + this.logger.error('Failed to write spotify config file: ' + err); } else { - conf += 'credentials: ' + os.EOL; - conf += ' type: zeroconf' + os.EOL; + defer.resolve(''); + this.logger.info('Spotify config file written'); } - - - - - fs.writeFile(configFileDestinationPath, conf, (err) => { - if (err) { - defer.reject(err); - this.logger.error('Failed to write spotify config file: ' + err); - } else { - defer.resolve(''); - this.logger.info('Spotify config file written'); - } - }); - return defer.promise; + }); + return defer.promise; }; ControllerSpotify.prototype.isOauthLoginAlreadyConfiguredOnDaemon = function () { - var self = this; + var self = this; - try { - var credentialsFile = fs.readFileSync(credentialsPath, {encoding: 'utf8'}).toString(); - } catch (e) { - self.logger.error('Failed to read credentials file: ' + e); - } + try { + var credentialsFile = fs.readFileSync(credentialsPath, { encoding: 'utf8' }).toString(); + } catch (e) { + self.logger.error('Failed to read credentials file: ' + e); + } - if (credentialsFile && credentialsFile.length > 0) { - return true; - } else { - return false; - } + if (credentialsFile && credentialsFile.length > 0) { + return true; + } else { + return false; + } }; ControllerSpotify.prototype.saveGoLibrespotSettings = function (data, avoidBroadcastUiConfig) { - var self = this; - var defer = libQ.defer(); - - var broadcastUiConfig = true; - if (avoidBroadcastUiConfig === true){ - broadcastUiConfig = false; - } + var self = this; + var defer = libQ.defer(); - if (data.bitrate !== undefined && data.bitrate.value !== undefined) { - self.config.set('bitrate_number', data.bitrate.value); - } + var broadcastUiConfig = true; + if (avoidBroadcastUiConfig === true) { + broadcastUiConfig = false; + } - if (data.debug !== undefined) { - self.config.set('debug', data.debug); - } - if (data.icon && data.icon.value !== undefined) { - self.config.set('icon', data.icon.value); - } - if (data.normalisation_pregain && data.normalisation_pregain.value !== undefined) { - self.config.set('normalisation_pregain', data.normalisation_pregain.value); - } + if (data.bitrate !== undefined && data.bitrate.value !== undefined) { + self.config.set('bitrate_number', data.bitrate.value); + } + if (data.debug !== undefined) { + self.config.set('debug', data.debug); + } + if (data.icon && data.icon.value !== undefined) { + self.config.set('icon', data.icon.value); + } + if (data.normalisation_pregain && data.normalisation_pregain.value !== undefined) { + self.config.set('normalisation_pregain', data.normalisation_pregain.value); + } - self.selectedBitrate = self.config.get('bitrate_number', '320').toString(); - self.initializeLibrespotDaemon(); + self.selectedBitrate = self.config.get('bitrate_number', '320').toString(); + self.initializeLibrespotDaemon(); - return defer.promise; + return defer.promise; }; // OAUTH ControllerSpotify.prototype.refreshAccessToken = function () { - var self = this; - var defer = libQ.defer(); - - var refreshToken = self.config.get('refresh_token', 'none'); - if (refreshToken !== 'none' && refreshToken !== null && refreshToken !== undefined) { - superagent.post('https://oauth-performer.dfs.volumio.org/spotify/accessToken') - .send({refreshToken: refreshToken}) - .then(function (results) { - if (results && results.body && results.body.accessToken) { - defer.resolve(results) - } else { - defer.resject('No access token received'); - } - }) - .catch(function (err) { - self.logger.info('An error occurred while refreshing Spotify Token ' + err); - }); - } + var self = this; + var defer = libQ.defer(); + + var refreshToken = self.config.get('refresh_token', 'none'); + if (refreshToken !== 'none' && refreshToken !== null && refreshToken !== undefined) { + superagent + .post('https://oauth-performer.dfs.volumio.org/spotify/accessToken') + .send({ refreshToken: refreshToken }) + .then(function (results) { + if (results && results.body && results.body.accessToken) { + defer.resolve(results); + } else { + defer.resject('No access token received'); + } + }) + .catch(function (err) { + self.logger.info('An error occurred while refreshing Spotify Token ' + err); + }); + } - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.spotifyClientCredentialsGrant = function () { - var self = this; - var defer = libQ.defer(); - var d = new Date(); - var now = d.getTime(); - - var refreshToken = self.config.get('refresh_token', 'none'); - if (refreshToken !== 'none' && refreshToken !== null && refreshToken !== undefined) { - self.spotifyApi.setRefreshToken(refreshToken); - self.refreshAccessToken() - .then(function (data) { - self.spotifyAccessToken = data.body['accessToken']; - self.debugLog('------------------------------------------------------ ACCESS TOKEN ------------------------------------------------------'); - self.debugLog(self.spotifyAccessToken); - self.debugLog('------------------------------------------------------ ACCESS TOKEN ------------------------------------------------------'); - self.config.set('access_token', self.spotifyAccessToken); - self.spotifyApi.setAccessToken(self.spotifyAccessToken); - self.spotifyAccessTokenExpiration = data.body['expiresInSeconds'] * 1000 + now; - self.logger.info('New Spotify access token = ' + self.spotifyAccessToken); - defer.resolve(); - }, function (err) { - self.logger.info('Spotify credentials grant failed with ' + err); - }); - } + var self = this; + var defer = libQ.defer(); + var d = new Date(); + var now = d.getTime(); + + var refreshToken = self.config.get('refresh_token', 'none'); + if (refreshToken !== 'none' && refreshToken !== null && refreshToken !== undefined) { + self.spotifyApi.setRefreshToken(refreshToken); + self.refreshAccessToken().then( + function (data) { + self.spotifyAccessToken = data.body['accessToken']; + self.debugLog( + '------------------------------------------------------ ACCESS TOKEN ------------------------------------------------------', + ); + self.debugLog(self.spotifyAccessToken); + self.debugLog( + '------------------------------------------------------ ACCESS TOKEN ------------------------------------------------------', + ); + self.config.set('access_token', self.spotifyAccessToken); + self.spotifyApi.setAccessToken(self.spotifyAccessToken); + self.spotifyAccessTokenExpiration = data.body['expiresInSeconds'] * 1000 + now; + self.logger.info('New Spotify access token = ' + self.spotifyAccessToken); + defer.resolve(); + }, + function (err) { + self.logger.info('Spotify credentials grant failed with ' + err); + }, + ); + } - return defer.promise; -} + return defer.promise; +}; ControllerSpotify.prototype.oauthLogin = function (data) { - var self=this; - - self.logger.info('Executing Spotify Oauth Login'); - - if (data && data.refresh_token) { - self.logger.info('Saving Spotify Refresh Token'); - self.config.set('refresh_token', data.refresh_token); - - self.spotifyApiConnect().then(function () { - self.config.set('credentials_type', 'spotify_token'); - self.initializeLibrespotDaemon(); - self.initializeSpotifyBrowsingFacility(); - var config = self.getUIConfig(); - config.then(function(conf) { - self.commandRouter.broadcastMessage('pushUiConfig', conf); - self.commandRouter.broadcastMessage('closeAllModals', ''); - defer.resolve(conf) - }); - }).fail(function (e) { - self.logger.error('Failed to perform Spotify API connection after OAUTH Login: ' + e); + var self = this; + + self.logger.info('Executing Spotify Oauth Login'); + + if (data && data.refresh_token) { + self.logger.info('Saving Spotify Refresh Token'); + self.config.set('refresh_token', data.refresh_token); + + self + .spotifyApiConnect() + .then(function () { + self.config.set('credentials_type', 'spotify_token'); + self.initializeLibrespotDaemon(); + self.initializeSpotifyBrowsingFacility(); + var config = self.getUIConfig(); + config.then(function (conf) { + self.commandRouter.broadcastMessage('pushUiConfig', conf); + self.commandRouter.broadcastMessage('closeAllModals', ''); + defer.resolve(conf); }); - } else { - self.logger.error('Could not receive oauth data'); - } + }) + .fail(function (e) { + self.logger.error('Failed to perform Spotify API connection after OAUTH Login: ' + e); + }); + } else { + self.logger.error('Could not receive oauth data'); + } }; ControllerSpotify.prototype.externalOauthLogin = function (data) { - var self=this; - var defer = libQ.defer(); - - if (data && data.refresh_token) { - self.logger.info('Saving Spotify Refresh Token'); - self.config.set('refresh_token', data.refresh_token); - self.spopDaemonConnect(); - setTimeout(()=>{ - defer.resolve(''); - },150); - } else { - self.logger.error('Could not receive oauth data'); - defer.resolve(''); - } - return defer.promise + var self = this; + var defer = libQ.defer(); + + if (data && data.refresh_token) { + self.logger.info('Saving Spotify Refresh Token'); + self.config.set('refresh_token', data.refresh_token); + self.spopDaemonConnect(); + setTimeout(() => { + defer.resolve(''); + }, 150); + } else { + self.logger.error('Could not receive oauth data'); + defer.resolve(''); + } + return defer.promise; }; ControllerSpotify.prototype.logout = function (avoidBroadcastUiConfig) { - var self=this; + var self = this; - var broadcastUiConfig = true; - if (avoidBroadcastUiConfig === true){ - broadcastUiConfig = false; - } - - self.deleteCredentialsFile(); - self.resetSpotifyCredentials(); - setTimeout(()=>{ - self.initializeLibrespotDaemon(); - }, 1000); + var broadcastUiConfig = true; + if (avoidBroadcastUiConfig === true) { + broadcastUiConfig = false; + } + self.deleteCredentialsFile(); + self.resetSpotifyCredentials(); + setTimeout(() => { + self.initializeLibrespotDaemon(); + }, 1000); - self.commandRouter.pushToastMessage('success', self.getI18n('LOGOUT'), self.getI18n('LOGOUT_SUCCESSFUL')); + self.commandRouter.pushToastMessage('success', self.getI18n('LOGOUT'), self.getI18n('LOGOUT_SUCCESSFUL')); - self.pushUiConfig(broadcastUiConfig); - self.removeToBrowseSources(); + self.pushUiConfig(broadcastUiConfig); + self.removeToBrowseSources(); }; ControllerSpotify.prototype.pushUiConfig = function (broadcastUiConfig) { - var self=this; - - setTimeout(()=>{ - var config = self.getUIConfig(); - config.then((conf)=> { - if (broadcastUiConfig) { - self.commandRouter.broadcastMessage('pushUiConfig', conf); - } - }); - }, 3000); + var self = this; + + setTimeout(() => { + var config = self.getUIConfig(); + config.then((conf) => { + if (broadcastUiConfig) { + self.commandRouter.broadcastMessage('pushUiConfig', conf); + } + }); + }, 3000); }; ControllerSpotify.prototype.resetSpotifyCredentials = function () { - var self=this; + var self = this; - self.config.set('logged_user_id', ''); - self.config.set('access_token', ''); - self.config.set('refresh_token', ''); - self.config.set('credentials_type', 'zeroconf'); + self.config.set('logged_user_id', ''); + self.config.set('access_token', ''); + self.config.set('refresh_token', ''); + self.config.set('credentials_type', 'zeroconf'); - if (self.spotifyApi) { - self.spotifyApi.resetCredentials(); - } + if (self.spotifyApi) { + self.spotifyApi.resetCredentials(); + } - self.accessToken = undefined; - self.spotifyAccessTokenExpiration = undefined; - self.loggedInUserId = undefined; + self.accessToken = undefined; + self.spotifyAccessTokenExpiration = undefined; + self.loggedInUserId = undefined; }; ControllerSpotify.prototype.deleteCredentialsFile = function () { - var self=this; + var self = this; - self.logger.info('Deleting Spotify credentials File'); - try { - fs.unlinkSync(credentialsPath) - } catch(err) { - self.logger.error('Failed to delete credentials file ' + e); - } + self.logger.info('Deleting Spotify credentials File'); + try { + fs.unlinkSync(credentialsPath); + } catch (err) { + self.logger.error('Failed to delete credentials file ' + e); + } }; ControllerSpotify.prototype.spotifyApiConnect = function () { - var self = this; - var defer = libQ.defer(); - var d = new Date(); - - self.spotifyApi = new SpotifyWebApi(); - - // Retrieve an access token - self.spotifyClientCredentialsGrant() + var self = this; + var defer = libQ.defer(); + var d = new Date(); + + self.spotifyApi = new SpotifyWebApi(); + + // Retrieve an access token + self.spotifyClientCredentialsGrant().then( + function (data) { + self.logger.info('Spotify credentials grant success - running version from March 24, 2019'); + self + .getUserInformations() .then(function (data) { - self.logger.info('Spotify credentials grant success - running version from March 24, 2019'); - self.getUserInformations().then(function (data) { - defer.resolve(); - }).fail(function (err) { - defer.reject(err); - self.logger.error('Spotify credentials failed to read user data: ' + err); - }); - }, function (err) { - self.logger.info('Spotify credentials grant failed with ' + err); - defer.reject(err); - } - ); + defer.resolve(); + }) + .fail(function (err) { + defer.reject(err); + self.logger.error('Spotify credentials failed to read user data: ' + err); + }); + }, + function (err) { + self.logger.info('Spotify credentials grant failed with ' + err); + defer.reject(err); + }, + ); - return defer.promise; -} + return defer.promise; +}; ControllerSpotify.prototype.refreshAccessToken = function () { - var self = this; - var defer = libQ.defer(); - - var refreshToken = self.config.get('refresh_token', 'none'); - if (refreshToken !== 'none' && refreshToken !== null && refreshToken !== undefined) { - superagent.post('https://oauth-performer.dfs.volumio.org/spotify/accessToken') - .send({refreshToken: refreshToken}) - .then(function (results) { - if (results && results.body && results.body.accessToken) { - defer.resolve(results) - } else { - defer.resject('No access token received'); - } - }) - .catch(function (err) { - self.logger.info('An error occurred while refreshing Spotify Token ' + err); - }); - } + var self = this; + var defer = libQ.defer(); + + var refreshToken = self.config.get('refresh_token', 'none'); + if (refreshToken !== 'none' && refreshToken !== null && refreshToken !== undefined) { + superagent + .post('https://oauth-performer.dfs.volumio.org/spotify/accessToken') + .send({ refreshToken: refreshToken }) + .then(function (results) { + if (results && results.body && results.body.accessToken) { + defer.resolve(results); + } else { + defer.resject('No access token received'); + } + }) + .catch(function (err) { + self.logger.info('An error occurred while refreshing Spotify Token ' + err); + }); + } - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.spotifyCheckAccessToken = function () { - var self = this; - var defer = libQ.defer(); - var d = new Date(); - var now = d.getTime(); - - if (self.spotifyAccessTokenExpiration < now) { - self.refreshAccessToken() - .then(function (data) { - self.spotifyAccessToken = data.body.accessToken; - self.spotifyApi.setAccessToken(data.body.accessToken); - self.spotifyAccessTokenExpiration = data.body.expiresInSeconds * 1000 + now; - self.logger.info('New access token = ' + self.spotifyAccessToken); - defer.resolve(); - }); - } else { - defer.resolve(); - } - - return defer.promise; + var self = this; + var defer = libQ.defer(); + var d = new Date(); + var now = d.getTime(); + + if (self.spotifyAccessTokenExpiration < now) { + self.refreshAccessToken().then(function (data) { + self.spotifyAccessToken = data.body.accessToken; + self.spotifyApi.setAccessToken(data.body.accessToken); + self.spotifyAccessTokenExpiration = data.body.expiresInSeconds * 1000 + now; + self.logger.info('New access token = ' + self.spotifyAccessToken); + defer.resolve(); + }); + } else { + defer.resolve(); + } + return defer.promise; }; ControllerSpotify.prototype.initializeSpotifyBrowsingFacility = function () { - var self = this; - - var refreshToken = self.config.get('refresh_token', 'none'); - if (refreshToken !== 'none' && refreshToken !== null && refreshToken !== undefined) { - self.spotifyApiConnect().then(function() { - self.logger.info('Spotify Successfully logged in'); - self.getRoot(); - self.addToBrowseSources(); - }).fail(function (err) { - self.logger.info('An error occurred while initializing Spotify Browsing facility: ' + err); - }); - } -} + var self = this; + + var refreshToken = self.config.get('refresh_token', 'none'); + if (refreshToken !== 'none' && refreshToken !== null && refreshToken !== undefined) { + self + .spotifyApiConnect() + .then(function () { + self.logger.info('Spotify Successfully logged in'); + self.getRoot(); + self.addToBrowseSources(); + }) + .fail(function (err) { + self.logger.info('An error occurred while initializing Spotify Browsing facility: ' + err); + }); + } +}; ControllerSpotify.prototype.getUserInformations = function () { - var self = this; - var defer = libQ.defer(); - - self.spotifyApi.getMe() - .then(function(data) { - if (data && data.body) { - self.debugLog('User informations: ' + JSON.stringify(data.body)); - self.loggedInUserId = data.body.id; - self.userCountry = data.body.country || 'US'; - self.config.set('logged_user_id', self.loggedInUserId); - self.isLoggedIn = true; - defer.resolve(''); - } - }, function(err) { - defer.reject(''); - self.logger.error('Failed to retrieve user informations: ' + err); - }); + var self = this; + var defer = libQ.defer(); + + self.spotifyApi.getMe().then( + function (data) { + if (data && data.body) { + self.debugLog('User informations: ' + JSON.stringify(data.body)); + self.loggedInUserId = data.body.id; + self.userCountry = data.body.country || 'US'; + self.config.set('logged_user_id', self.loggedInUserId); + self.isLoggedIn = true; + defer.resolve(''); + } + }, + function (err) { + defer.reject(''); + self.logger.error('Failed to retrieve user informations: ' + err); + }, + ); - return defer.promise; + return defer.promise; }; // CACHE -ControllerSpotify.prototype.flushCache = function() { - var self=this +ControllerSpotify.prototype.flushCache = function () { + var self = this; - self.browseCache.flushAll(); -} + self.browseCache.flushAll(); +}; // ALBUMART ControllerSpotify.prototype._getAlbumArt = function (item) { - - var albumart = ''; - if (item.hasOwnProperty('images') && item.images.length > 0) { - albumart = item.images[0].url; - } - return albumart; + var albumart = ''; + if (item.hasOwnProperty('images') && item.images.length > 0) { + albumart = item.images[0].url; + } + return albumart; }; ControllerSpotify.prototype.getAlbumArt = function (data, path) { + var artist, album; - var artist, album; + if (data != undefined && data.path != undefined) { + path = data.path; + } - if (data != undefined && data.path != undefined) { - path = data.path; - } - - var web; + var web; - if (data != undefined && data.artist != undefined) { - artist = data.artist; - if (data.album != undefined) - album = data.album; - else album = data.artist; + if (data != undefined && data.artist != undefined) { + artist = data.artist; + if (data.album != undefined) album = data.album; + else album = data.artist; - web = '?web=' + encodeURIComponent(artist) + '/' + encodeURIComponent(album) + '/large' - } + web = '?web=' + encodeURIComponent(artist) + '/' + encodeURIComponent(album) + '/large'; + } - var url = '/albumart'; + var url = '/albumart'; - if (web != undefined) - url = url + web; + if (web != undefined) url = url + web; - if (web != undefined && path != undefined) - url = url + '&'; - else if (path != undefined) - url = url + '?'; + if (web != undefined && path != undefined) url = url + '&'; + else if (path != undefined) url = url + '?'; - if (path != undefined) - url = url + 'path=' + encodeURIComponent(path); + if (path != undefined) url = url + 'path=' + encodeURIComponent(path); - return url; + return url; }; // TRANSLATIONS ControllerSpotify.prototype.loadI18n = function () { - var self=this; + var self = this; - try { - var language_code = this.commandRouter.sharedVars.get('language_code'); - self.i18n=fs.readJsonSync(__dirname+'/i18n/strings_'+language_code+".json"); - } catch(e) { - self.i18n=fs.readJsonSync(__dirname+'/i18n/strings_en.json'); - } + try { + var language_code = this.commandRouter.sharedVars.get('language_code'); + self.i18n = fs.readJsonSync(__dirname + '/i18n/strings_' + language_code + '.json'); + } catch (e) { + self.i18n = fs.readJsonSync(__dirname + '/i18n/strings_en.json'); + } - self.i18nDefaults=fs.readJsonSync(__dirname+'/i18n/strings_en.json'); + self.i18nDefaults = fs.readJsonSync(__dirname + '/i18n/strings_en.json'); }; ControllerSpotify.prototype.getI18n = function (key) { - var self=this; - - if (key.indexOf('.') > 0) { - var mainKey = key.split('.')[0]; - var secKey = key.split('.')[1]; - if (self.i18n[mainKey][secKey] !== undefined) { - return self.i18n[mainKey][secKey]; - } else { - return self.i18nDefaults[mainKey][secKey]; - } + var self = this; + if (key.indexOf('.') > 0) { + var mainKey = key.split('.')[0]; + var secKey = key.split('.')[1]; + if (self.i18n[mainKey][secKey] !== undefined) { + return self.i18n[mainKey][secKey]; } else { - if (self.i18n[key] !== undefined) { - return self.i18n[key]; - } else { - return self.i18nDefaults[key]; - } - + return self.i18nDefaults[mainKey][secKey]; + } + } else { + if (self.i18n[key] !== undefined) { + return self.i18n[key]; + } else { + return self.i18nDefaults[key]; } + } }; - // BROWSING ControllerSpotify.prototype.addToBrowseSources = function () { - var data = { - name: 'Spotify', - uri: 'spotify', - plugin_type: 'music_service', - plugin_name: 'spop', - albumart: '/albumart?sourceicon=music_service/spop/spotify.png' - }; - this.commandRouter.volumioAddToBrowseSources(data); + var data = { + name: 'Spotify', + uri: 'spotify', + plugin_type: 'music_service', + plugin_name: 'spop', + albumart: '/albumart?sourceicon=music_service/spop/spotify.png', + }; + this.commandRouter.volumioAddToBrowseSources(data); }; ControllerSpotify.prototype.removeToBrowseSources = function () { - - this.commandRouter.volumioRemoveToBrowseSources('Spotify'); + this.commandRouter.volumioRemoveToBrowseSources('Spotify'); }; - ControllerSpotify.prototype.handleBrowseUri = function (curUri) { - var self = this; - - self.commandRouter.logger.info('In handleBrowseUri, curUri=' + curUri); - var response; - - if (curUri.startsWith('spotify')) { - if (curUri == 'spotify') { - response = self.getRoot(); - } else if (curUri.startsWith('spotify/playlists')) { - if (curUri == 'spotify/playlists') - response = self.getMyPlaylists(curUri); // use the Spotify Web API instead of the spop service - else { - response = self.listWebPlaylist(curUri); // use the function to list playlists returned from the Spotify Web API - } - } else if (curUri.startsWith('spotify/myalbums')) { - response = self.getMyAlbums(curUri); - } else if (curUri.startsWith('spotify/mytracks')) { - response = self.getMyTracks(curUri); - } else if (curUri.startsWith('spotify/myartists')) { - response = self.getMyArtists(curUri); - } else if (curUri.startsWith('spotify/mytopartists')) { - response = self.getTopArtists(curUri); - } else if (curUri.startsWith('spotify/mytoptracks')) { - response = self.getTopTracks(curUri); - } else if (curUri.startsWith('spotify/myrecentlyplayedtracks')) { - response = self.getRecentTracks(curUri); - } else if (curUri.startsWith('spotify/featuredplaylists')) { - response = self.featuredPlaylists(curUri); - } else if (curUri.startsWith('spotify:user:')) { - response = self.listWebPlaylist(curUri); - } else if (curUri.startsWith('spotify:playlist:')) { - var uriSplitted = curUri.split(':'); - response = self.listWebPlaylist('spotify:user:spotify:playlist:' + uriSplitted[2]); - } else if (curUri.startsWith('spotify/new')) { - response = self.listWebNew(curUri); - } else if (curUri.startsWith('spotify/categories')) { - response = self.listWebCategories(curUri); - } else if (curUri.startsWith('spotify:album')) { - response = self.listWebAlbum(curUri); - } else if (curUri.startsWith('spotify/category')) { - response = self.listWebCategory(curUri); - } else if (curUri.startsWith('spotify:artist:')) { - response = self.listWebArtist(curUri); - } - else { - self.logger.info('************* Bad browse Uri:' + curUri); - } + var self = this; + + self.commandRouter.logger.info('In handleBrowseUri, curUri=' + curUri); + var response; + + if (curUri.startsWith('spotify')) { + if (curUri == 'spotify') { + response = self.getRoot(); + } else if (curUri.startsWith('spotify/playlists')) { + if (curUri == 'spotify/playlists') + response = self.getMyPlaylists(curUri); // use the Spotify Web API instead of the spop service + else { + response = self.listWebPlaylist(curUri); // use the function to list playlists returned from the Spotify Web API + } + } else if (curUri.startsWith('spotify/myalbums')) { + response = self.getMyAlbums(curUri); + } else if (curUri.startsWith('spotify/mytracks')) { + response = self.getMyTracks(curUri); + } else if (curUri.startsWith('spotify/myartists')) { + response = self.getMyArtists(curUri); + } else if (curUri.startsWith('spotify/mytopartists')) { + response = self.getTopArtists(curUri); + } else if (curUri.startsWith('spotify/mytoptracks')) { + response = self.getTopTracks(curUri); + } else if (curUri.startsWith('spotify/myrecentlyplayedtracks')) { + response = self.getRecentTracks(curUri); + } else if (curUri.startsWith('spotify/featuredplaylists')) { + response = self.featuredPlaylists(curUri); + } else if (curUri.startsWith('spotify:user:')) { + response = self.listWebPlaylist(curUri); + } else if (curUri.startsWith('spotify:playlist:')) { + var uriSplitted = curUri.split(':'); + response = self.listWebPlaylist('spotify:user:spotify:playlist:' + uriSplitted[2]); + } else if (curUri.startsWith('spotify/new')) { + response = self.listWebNew(curUri); + } else if (curUri.startsWith('spotify/categories')) { + response = self.listWebCategories(curUri); + } else if (curUri.startsWith('spotify:album')) { + response = self.listWebAlbum(curUri); + } else if (curUri.startsWith('spotify/category')) { + response = self.listWebCategory(curUri); + } else if (curUri.startsWith('spotify:artist:')) { + response = self.listWebArtist(curUri); + } else { + self.logger.info('************* Bad browse Uri:' + curUri); } + } - return response; + return response; }; ControllerSpotify.prototype.getRoot = function () { - var self = this; - var defer = libQ.defer(); - - self.browseCache.get('root',function( err, value ){ - if( !err ){ - // Root has not been cached yet - if(value == undefined){ - self.listRoot().then((data)=>{ - // Set root cache - self.browseCache.set('root',data) - defer.resolve(data) - }); - } else { - // Cached Root - defer.resolve(value) - } - } else { - self.logger.error('Could not fetch root spotify folder cached data: ' + err); - } - }); + var self = this; + var defer = libQ.defer(); + + self.browseCache.get('root', function (err, value) { + if (!err) { + // Root has not been cached yet + if (value == undefined) { + self.listRoot().then((data) => { + // Set root cache + self.browseCache.set('root', data); + defer.resolve(data); + }); + } else { + // Cached Root + defer.resolve(value); + } + } else { + self.logger.error('Could not fetch root spotify folder cached data: ' + err); + } + }); - return defer.promise + return defer.promise; }; ControllerSpotify.prototype.listRoot = function (curUri) { - var self = this; - var defer = libQ.defer(); + var self = this; + var defer = libQ.defer(); + + var response = { + navigation: { + lists: [ + { + availableListViews: ['grid', 'list'], + type: 'title', + title: self.getI18n('MY_MUSIC'), + items: [ + { + service: 'spop', + type: 'streaming-category', + title: self.getI18n('MY_PLAYLISTS'), + artist: '', + album: '', + albumart: '/albumart?sourceicon=music_service/spop/icons/playlist.png', + uri: 'spotify/playlists', + }, + { + service: 'spop', + type: 'streaming-category', + title: self.getI18n('MY_ALBUMS'), + artist: '', + album: '', + albumart: '/albumart?sourceicon=music_service/spop/icons/album.png', + uri: 'spotify/myalbums', + }, + { + service: 'spop', + type: 'streaming-category', + title: self.getI18n('MY_TRACKS'), + artist: '', + album: '', + albumart: '/albumart?sourceicon=music_service/spop/icons/track.png', + uri: 'spotify/mytracks', + }, + { + service: 'spop', + type: 'streaming-category', + title: self.getI18n('MY_ARTISTS'), + artist: '', + album: '', + albumart: '/albumart?sourceicon=music_service/spop/icons/artist.png', + uri: 'spotify/myartists', + }, + { + service: 'spop', + type: 'streaming-category', + title: self.getI18n('MY_TOP_TRACKS'), + artist: '', + album: '', + albumart: '/albumart?sourceicon=music_service/spop/icons/track.png', + uri: 'spotify/mytoptracks', + }, + { + service: 'spop', + type: 'streaming-category', + title: self.getI18n('MY_TOP_ARTISTS'), + artist: '', + album: '', + albumart: '/albumart?sourceicon=music_service/spop/icons/artist.png', + uri: 'spotify/mytopartists', + }, + { + service: 'spop', + type: 'streaming-category', + title: self.getI18n('MY_RECENTLY_PLAYED_TRACKS'), + artist: '', + album: '', + albumart: '/albumart?sourceicon=music_service/spop/icons/track.png', + uri: 'spotify/myrecentlyplayedtracks', + }, + ], + }, + ], + }, + }; + + var spotifyRootArray = [ + self.featuredPlaylists('spotify/featuredplaylists'), + self.listWebNew('spotify/new'), + self.listWebCategories('spotify/categories'), + ]; + libQ + .all(spotifyRootArray) + .then(function (results) { + var discoveryArray = [ + { + availableListViews: ['grid', 'list'], + type: 'title', + title: self.getI18n('FEATURED_PLAYLISTS'), + items: results[0].navigation.lists[0].items, + }, + { + availableListViews: ['grid', 'list'], + type: 'title', + title: self.getI18n('WHATS_NEW'), + items: results[1].navigation.lists[0].items, + }, + { + availableListViews: ['grid', 'list'], + type: 'title', + title: self.getI18n('GENRES_AND_MOODS'), + items: results[2].navigation.lists[0].items, + }, + ]; + response.navigation.lists = response.navigation.lists.concat(discoveryArray); + defer.resolve(response); + }) + .fail(function (err) { + self.logger.info('An error occurred while getting Spotify ROOT Discover Folders: ' + err); + defer.resolve(response); + }); - var response = { - navigation: { - lists: [ - { - "availableListViews": [ - "grid","list" - ], - "type": "title", - "title": self.getI18n('MY_MUSIC'), - "items": [ - { - service: 'spop', - type: 'streaming-category', - title: self.getI18n('MY_PLAYLISTS'), - artist: '', - album: '', - albumart: '/albumart?sourceicon=music_service/spop/icons/playlist.png', - uri: 'spotify/playlists' - }, - { - service: 'spop', - type: 'streaming-category', - title: self.getI18n('MY_ALBUMS'), - artist: '', - album: '', - albumart: '/albumart?sourceicon=music_service/spop/icons/album.png', - uri: 'spotify/myalbums' - }, - { - service: 'spop', - type: 'streaming-category', - title: self.getI18n('MY_TRACKS'), - artist: '', - album: '', - albumart: '/albumart?sourceicon=music_service/spop/icons/track.png', - uri: 'spotify/mytracks' - }, - { - service: 'spop', - type: 'streaming-category', - title: self.getI18n('MY_ARTISTS'), - artist: '', - album: '', - albumart: '/albumart?sourceicon=music_service/spop/icons/artist.png', - uri: 'spotify/myartists', - }, - { - service: 'spop', - type: 'streaming-category', - title: self.getI18n('MY_TOP_TRACKS'), - artist: '', - album: '', - albumart: '/albumart?sourceicon=music_service/spop/icons/track.png', - uri: 'spotify/mytoptracks' - }, - { - service: 'spop', - type: 'streaming-category', - title: self.getI18n('MY_TOP_ARTISTS'), - artist: '', - album: '', - albumart: '/albumart?sourceicon=music_service/spop/icons/artist.png', - uri: 'spotify/mytopartists' - }, - { - service: 'spop', - type: 'streaming-category', - title: self.getI18n('MY_RECENTLY_PLAYED_TRACKS'), - artist: '', - album: '', - albumart: '/albumart?sourceicon=music_service/spop/icons/track.png', - uri: 'spotify/myrecentlyplayedtracks' - } - ] - } - ] - } - } + return defer.promise; +}; - var spotifyRootArray = [self.featuredPlaylists('spotify/featuredplaylists'),self.listWebNew('spotify/new'),self.listWebCategories('spotify/categories')]; - libQ.all(spotifyRootArray) - .then(function (results) { - - var discoveryArray = [ - { - "availableListViews": [ - "grid","list" - ], - "type": "title", - "title": self.getI18n('FEATURED_PLAYLISTS'), - "items": results[0].navigation.lists[0].items - }, - { - "availableListViews": [ - "grid","list" - ], - "type": "title", - "title": self.getI18n('WHATS_NEW'), - "items": results[1].navigation.lists[0].items - }, - { - "availableListViews": [ - "grid","list" - ], - "type": "title", - "title": self.getI18n('GENRES_AND_MOODS'), - "items": results[2].navigation.lists[0].items - } - ]; - response.navigation.lists = response.navigation.lists.concat(discoveryArray); - defer.resolve(response); - }) - .fail(function (err) { - self.logger.info('An error occurred while getting Spotify ROOT Discover Folders: ' + err); - defer.resolve(response); - }); - - return defer.promise; -} - - -ControllerSpotify.prototype.getMyPlaylists = function (curUri) { - var self = this; - var defer = libQ.defer(); - - self.spotifyCheckAccessToken() - .then(function (data) { +ControllerSpotify.prototype.getMyPlaylists = function (curUri) { + var self = this; + var defer = libQ.defer(); + self.spotifyCheckAccessToken().then(function (data) { + var response = { + navigation: { + prev: { + uri: 'spotify', + }, + lists: [ + { + availableListViews: ['list', 'grid'], + items: [], + }, + ], + }, + }; + self.spotifyApi.getUserPlaylists(self.loggedInUserId, { limit: 50 }).then( + function (results) { + for (var i in results.body.items) { + var playlist = results.body.items[i]; + response.navigation.lists[0].items.push({ + service: 'spop', + type: 'playlist', + title: playlist.name, + albumart: self._getAlbumArt(playlist), + uri: 'spotify:user:spotify:playlist:' + playlist.id, + }); + } - var response = { - navigation: { - prev: { - uri: 'spotify' - }, - "lists": [ - { - "availableListViews": [ - "list", - "grid" - ], - "items": [] - } - ] - } - }; - self.spotifyApi.getUserPlaylists(self.loggedInUserId, { limit: 50 }) - .then(function(results) { - for (var i in results.body.items) { - var playlist = results.body.items[i]; - response.navigation.lists[0].items.push({ - service: 'spop', - type: 'playlist', - title: playlist.name, - albumart: self._getAlbumArt(playlist), - uri: 'spotify:user:spotify:playlist:' + playlist.id - }); - } - - defer.resolve(response); - },function(err) { - defer.reject('An error listing Spotify Playlists ' + err.message) - self.logger.info('An error occurred while listing Spotify getMyPlaylists ' + err.message); - }); - } - ); + defer.resolve(response); + }, + function (err) { + defer.reject('An error listing Spotify Playlists ' + err.message); + self.logger.info('An error occurred while listing Spotify getMyPlaylists ' + err.message); + }, + ); + }); - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.getMyAlbums = function () { - const defer = libQ.defer(); - const albums = []; - - this.spotifyCheckAccessToken().then(() => { - fetchPagedData( - this.spotifyApi, - 'getMySavedAlbums', - {}, - { - onData: (items) => { - for (var i in items) { - var album = items[i].album; - albums.push({ - service: 'spop', - type: 'folder', - title: album.name, - albumart: this._getAlbumArt(album), - uri: album.uri - }); - } - }, - onEnd: () => { - albums.sort((a, b) => { - if (a.artist !== b.artist) { - return a.artist > b.artist ? 1 : -1; - } - return a.year > b.year ? 1 : a.year === b.year ? 0 : -1; - }); - defer.resolve({ - navigation: { - prev: { - uri: 'spotify', - }, - lists: [ - { - availableListViews: ['list', 'grid'], - items: albums, - }, - ], - }, - }); - }, + const defer = libQ.defer(); + const albums = []; + + this.spotifyCheckAccessToken().then(() => { + fetchPagedData( + this.spotifyApi, + 'getMySavedAlbums', + {}, + { + onData: (items) => { + for (var i in items) { + var album = items[i].album; + albums.push({ + service: 'spop', + type: 'folder', + title: album.name, + albumart: this._getAlbumArt(album), + uri: album.uri, + }); + } + }, + onEnd: () => { + albums.sort((a, b) => { + if (a.artist !== b.artist) { + return a.artist > b.artist ? 1 : -1; } - ).catch((err) => { - this.logger.error('An error occurred while listing Spotify my albums ' + err); - defer.reject(''); - }); + return a.year > b.year ? 1 : a.year === b.year ? 0 : -1; + }); + defer.resolve({ + navigation: { + prev: { + uri: 'spotify', + }, + lists: [ + { + availableListViews: ['list', 'grid'], + items: albums, + }, + ], + }, + }); + }, + }, + ).catch((err) => { + this.logger.error('An error occurred while listing Spotify my albums ' + err); + defer.reject(''); }); + }); - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.getMyTracks = function () { - const defer = libQ.defer(); - const tracks = []; - - this.spotifyCheckAccessToken().then(() => { - fetchPagedData( - this.spotifyApi, - 'getMySavedTracks', - {}, - { - onData: (items) => { - for (var i in items) { - var track = items[i].track; - if (this.isTrackAvailableInCountry(track)) { - tracks.push({ - service: 'spop', - type: 'song', - title: track.name, - artist: track.artists[0] ? track.artists[0].name : null, - album: track.album.name || null, - albumart: this._getAlbumArt(track.album), - uri: track.uri - }); - } - } - }, - onEnd: () => { - tracks.sort((a, b) => { - if (a.artist !== b.artist) { - return a.artist > b.artist ? 1 : -1; - } - if (a.year !== b.year) { - return a.year > b.year ? 1 : -1; - } - if (a.album !== b.album) { - return a.album > b.album ? 1 : -1; - } - return a.tracknumber > b.tracknumber ? 1 : a.tracknumber === b.tracknumber ? 0 : -1; - }); - defer.resolve({ - navigation: { - prev: { - uri: 'spotify', - }, - lists: [ - { - availableListViews: ['list'], - items: tracks, - }, - ], - }, - }); - }, + const defer = libQ.defer(); + const tracks = []; + + this.spotifyCheckAccessToken().then(() => { + fetchPagedData( + this.spotifyApi, + 'getMySavedTracks', + {}, + { + onData: (items) => { + for (var i in items) { + var track = items[i].track; + if (this.isTrackAvailableInCountry(track)) { + tracks.push({ + service: 'spop', + type: 'song', + title: track.name, + artist: track.artists[0] ? track.artists[0].name : null, + album: track.album.name || null, + albumart: this._getAlbumArt(track.album), + uri: track.uri, + }); } - ).catch((err) => { - this.logger.error('An error occurred while listing Spotify my tracks ' + err); - defer.reject(''); - }); + } + }, + onEnd: () => { + tracks.sort((a, b) => { + if (a.artist !== b.artist) { + return a.artist > b.artist ? 1 : -1; + } + if (a.year !== b.year) { + return a.year > b.year ? 1 : -1; + } + if (a.album !== b.album) { + return a.album > b.album ? 1 : -1; + } + return a.tracknumber > b.tracknumber ? 1 : a.tracknumber === b.tracknumber ? 0 : -1; + }); + defer.resolve({ + navigation: { + prev: { + uri: 'spotify', + }, + lists: [ + { + availableListViews: ['list'], + items: tracks, + }, + ], + }, + }); + }, + }, + ).catch((err) => { + this.logger.error('An error occurred while listing Spotify my tracks ' + err); + defer.reject(''); }); - return defer.promise; + }); + return defer.promise; }; ControllerSpotify.prototype.getMyArtists = function () { - const defer = libQ.defer(); - const artists = []; - - this.spotifyCheckAccessToken().then(() => { - fetchPagedData( - this.spotifyApi, - 'getFollowedArtists', - { paginationType: 'after' }, - { - getItems: (data) => data.body?.artists?.items || [], - onData: (items) => { - for (var i in items) { - const artist = items[i]; - artists.push({ - service: 'spop', - type: 'folder', - title: artist.name, - albumart: this._getAlbumArt(artist), - uri: artist.uri, - }); - } - }, - onEnd: () => { - artists.sort((a, b) => (a.title > b.title ? 1 : a.title === b.title ? 0 : -1)); - defer.resolve({ - navigation: { - prev: { - uri: 'spotify', - }, - lists: [ - { - availableListViews: ['list', 'grid'], - items: artists, - }, - ], - }, - }); + const defer = libQ.defer(); + const artists = []; + + this.spotifyCheckAccessToken().then(() => { + fetchPagedData( + this.spotifyApi, + 'getFollowedArtists', + { paginationType: 'after' }, + { + getItems: (data) => data.body?.artists?.items || [], + onData: (items) => { + for (var i in items) { + const artist = items[i]; + artists.push({ + service: 'spop', + type: 'folder', + title: artist.name, + albumart: this._getAlbumArt(artist), + uri: artist.uri, + }); + } + }, + onEnd: () => { + artists.sort((a, b) => (a.title > b.title ? 1 : a.title === b.title ? 0 : -1)); + defer.resolve({ + navigation: { + prev: { + uri: 'spotify', + }, + lists: [ + { + availableListViews: ['list', 'grid'], + items: artists, }, - } - ).catch((err) => { - this.logger.error('An error occurred while listing Spotify my artists ' + err); - this.handleBrowsingError(err); - defer.reject(''); - }); + ], + }, + }); + }, + }, + ).catch((err) => { + this.logger.error('An error occurred while listing Spotify my artists ' + err); + this.handleBrowsingError(err); + defer.reject(''); }); + }); - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.getTopArtists = function (curUri) { - var self = this; + var self = this; - var defer = libQ.defer(); + var defer = libQ.defer(); - self.spotifyCheckAccessToken() - .then(function (data) { - var spotifyDefer = self.spotifyApi.getMyTopArtists({limit: 50}); - spotifyDefer.then(function (results) { - var response = { - navigation: { - prev: { - uri: 'spotify' - }, - "lists": [ - { - "availableListViews": [ - "list", - "grid" - ], - "items": [] - } - ] - } - }; - - for (var i in results.body.items) { - var artist = results.body.items[i]; - response.navigation.lists[0].items.push({ - service: 'spop', - type: 'folder', - title: artist.name, - albumart: self._getAlbumArt(artist), - uri: artist.uri - }); - } - defer.resolve(response); - }, function (err) { - self.logger.error('An error occurred while listing Spotify my artists ' + err); - defer.reject(''); - }); - } - ); + self.spotifyCheckAccessToken().then(function (data) { + var spotifyDefer = self.spotifyApi.getMyTopArtists({ limit: 50 }); + spotifyDefer.then( + function (results) { + var response = { + navigation: { + prev: { + uri: 'spotify', + }, + lists: [ + { + availableListViews: ['list', 'grid'], + items: [], + }, + ], + }, + }; - return defer.promise; + for (var i in results.body.items) { + var artist = results.body.items[i]; + response.navigation.lists[0].items.push({ + service: 'spop', + type: 'folder', + title: artist.name, + albumart: self._getAlbumArt(artist), + uri: artist.uri, + }); + } + defer.resolve(response); + }, + function (err) { + self.logger.error('An error occurred while listing Spotify my artists ' + err); + defer.reject(''); + }, + ); + }); + + return defer.promise; }; ControllerSpotify.prototype.getTopTracks = function (curUri) { + var self = this; - var self = this; + var defer = libQ.defer(); - var defer = libQ.defer(); + self.spotifyCheckAccessToken().then(function (data) { + var spotifyDefer = self.spotifyApi.getMyTopTracks({ limit: 50 }); + spotifyDefer.then( + function (results) { + var response = { + navigation: { + prev: { + uri: 'spotify', + }, + lists: [ + { + availableListViews: ['list'], + items: [], + }, + ], + }, + }; - self.spotifyCheckAccessToken() - .then(function (data) { - var spotifyDefer = self.spotifyApi.getMyTopTracks({limit: 50}); - spotifyDefer.then(function (results) { - var response = { - navigation: { - prev: { - uri: 'spotify' - }, - "lists": [ - { - "availableListViews": [ - "list" - ], - "items": [] - } - ] - } - }; - - for (var i in results.body.items) { - var track = results.body.items[i]; - if (self.isTrackAvailableInCountry(track)) { - response.navigation.lists[0].items.push({ - service: 'spop', - type: 'song', - title: track.name, - artist: track.artists[0].name || null, - album: track.album.name || null, - albumart: self._getAlbumArt(track.album), - uri: track.uri - }); - } - } - defer.resolve(response); - }, function (err) { - self.logger.error('An error occurred while listing Spotify top tracks ' + err); - defer.reject(''); - }); - } - ); + for (var i in results.body.items) { + var track = results.body.items[i]; + if (self.isTrackAvailableInCountry(track)) { + response.navigation.lists[0].items.push({ + service: 'spop', + type: 'song', + title: track.name, + artist: track.artists[0].name || null, + album: track.album.name || null, + albumart: self._getAlbumArt(track.album), + uri: track.uri, + }); + } + } + defer.resolve(response); + }, + function (err) { + self.logger.error('An error occurred while listing Spotify top tracks ' + err); + defer.reject(''); + }, + ); + }); - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.getRecentTracks = function (curUri) { + var self = this; - var self = this; + var defer = libQ.defer(); - var defer = libQ.defer(); + self.spotifyCheckAccessToken().then(function (data) { + var spotifyDefer = self.spotifyApi.getMyRecentlyPlayedTracks({ limit: 50 }); + spotifyDefer.then( + function (results) { + var response = { + navigation: { + prev: { + uri: 'spotify', + }, + lists: [ + { + availableListViews: ['list'], + items: [], + }, + ], + }, + }; - self.spotifyCheckAccessToken() - .then(function (data) { - var spotifyDefer = self.spotifyApi.getMyRecentlyPlayedTracks({limit: 50}); - spotifyDefer.then(function (results) { - var response = { - navigation: { - prev: { - uri: 'spotify' - }, - "lists": [ - { - "availableListViews": [ - "list" - ], - "items": [] - } - ] - } - }; - - for (var i in results.body.items) { - var track = results.body.items[i].track; - if (self.isTrackAvailableInCountry(track)) { - response.navigation.lists[0].items.push({ - service: 'spop', - type: 'song', - title: track.name, - artist: track.artists[0].name || null, - album: track.album.name || null, - albumart: self._getAlbumArt(track.album), - uri: track.uri - }); - } - } - defer.resolve(response); - }, function (err) { - self.logger.error('An error occurred while listing Spotify recent tracks ' + err); - defer.reject(''); - }); - } - ); + for (var i in results.body.items) { + var track = results.body.items[i].track; + if (self.isTrackAvailableInCountry(track)) { + response.navigation.lists[0].items.push({ + service: 'spop', + type: 'song', + title: track.name, + artist: track.artists[0].name || null, + album: track.album.name || null, + albumart: self._getAlbumArt(track.album), + uri: track.uri, + }); + } + } + defer.resolve(response); + }, + function (err) { + self.logger.error('An error occurred while listing Spotify recent tracks ' + err); + defer.reject(''); + }, + ); + }); - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.featuredPlaylists = function (curUri) { + var self = this; - var self = this; + var defer = libQ.defer(); - var defer = libQ.defer(); + self.spotifyCheckAccessToken().then(function (data) { + var spotifyDefer = self.spotifyApi.getFeaturedPlaylists(); + spotifyDefer.then( + function (results) { + var response = { + navigation: { + prev: { + uri: 'spotify', + }, + lists: [ + { + availableListViews: ['list', 'grid'], + items: [], + }, + ], + }, + }; - self.spotifyCheckAccessToken() - .then(function (data) { - var spotifyDefer = self.spotifyApi.getFeaturedPlaylists(); - spotifyDefer.then(function (results) { - var response = { - navigation: { - prev: { - uri: 'spotify' - }, - "lists": [ - { - "availableListViews": [ - "list", - "grid" - ], - "items": [] - } - ] - } - }; - - for (var i in results.body.playlists.items) { - var playlist = results.body.playlists.items[i]; - response.navigation.lists[0].items.push({ - service: 'spop', - type: 'playlist', - title: playlist.name, - albumart: self._getAlbumArt(playlist), - uri: playlist.uri - }); - } - defer.resolve(response); - }, function (err) { - self.logger.error('An error occurred while listing Spotify featured playlists ' + err); - defer.reject(''); - }); - } - ); + for (var i in results.body.playlists.items) { + var playlist = results.body.playlists.items[i]; + response.navigation.lists[0].items.push({ + service: 'spop', + type: 'playlist', + title: playlist.name, + albumart: self._getAlbumArt(playlist), + uri: playlist.uri, + }); + } + defer.resolve(response); + }, + function (err) { + self.logger.error('An error occurred while listing Spotify featured playlists ' + err); + defer.reject(''); + }, + ); + }); - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.listWebPlaylist = function (curUri) { - var self = this; + var self = this; - var defer = libQ.defer(); + var defer = libQ.defer(); - var uriSplitted = curUri.split(':'); + var uriSplitted = curUri.split(':'); - var spotifyDefer = self.getPlaylistTracks(uriSplitted[2], uriSplitted[4]); - spotifyDefer.then(function (results) { - var response = { - navigation: { - prev: { - uri: 'spotify' - }, - "lists": [ - { - "availableListViews": [ - "list" - ], - "items": [] - } - ] - } - }; - for (var i in results) { - response.navigation.lists[0].items.push(results[i]); - } - var playlistInfo = self.getPlaylistInfo(uriSplitted[2], uriSplitted[4]); - playlistInfo.then(function (results) { - response.navigation.info = results; - response.navigation.info.uri = curUri; - response.navigation.info.service = 'spop'; - defer.resolve(response); - }) + var spotifyDefer = self.getPlaylistTracks(uriSplitted[2], uriSplitted[4]); + spotifyDefer.then(function (results) { + var response = { + navigation: { + prev: { + uri: 'spotify', + }, + lists: [ + { + availableListViews: ['list'], + items: [], + }, + ], + }, + }; + for (var i in results) { + response.navigation.lists[0].items.push(results[i]); + } + var playlistInfo = self.getPlaylistInfo(uriSplitted[2], uriSplitted[4]); + playlistInfo.then(function (results) { + response.navigation.info = results; + response.navigation.info.uri = curUri; + response.navigation.info.service = 'spop'; + defer.resolve(response); }); + }); - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.listWebNew = function (curUri) { + var self = this; - var self = this; + var defer = libQ.defer(); - var defer = libQ.defer(); + self.spotifyCheckAccessToken().then(function (data) { + var spotifyDefer = self.spotifyApi.getNewReleases({ limit: 50 }); + spotifyDefer.then( + function (results) { + var response = { + navigation: { + prev: { + uri: 'spotify', + }, + lists: [ + { + availableListViews: ['list', 'grid'], + items: [], + }, + ], + }, + }; - self.spotifyCheckAccessToken() - .then(function (data) { - var spotifyDefer = self.spotifyApi.getNewReleases({limit: 50}); - spotifyDefer.then(function (results) { - - var response = { - navigation: { - prev: { - uri: 'spotify' - }, - "lists": [ - { - "availableListViews": [ - "list", - "grid" - ], - "items": [] - } - ] - } - }; - - for (var i in results.body.albums.items) { - var album = results.body.albums.items[i]; - response.navigation.lists[0].items.push({ - service: 'spop', - type: 'folder', - title: album.name, - albumart: self._getAlbumArt(album), - uri: album.uri - }); - } - defer.resolve(response); - }, function (err) { - self.logger.error('An error occurred while listing Spotify new albums ' + err); - defer.reject(''); - }); - }); + for (var i in results.body.albums.items) { + var album = results.body.albums.items[i]; + response.navigation.lists[0].items.push({ + service: 'spop', + type: 'folder', + title: album.name, + albumart: self._getAlbumArt(album), + uri: album.uri, + }); + } + defer.resolve(response); + }, + function (err) { + self.logger.error('An error occurred while listing Spotify new albums ' + err); + defer.reject(''); + }, + ); + }); - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.listWebAlbum = function (curUri) { - var self = this; - var defer = libQ.defer(); - var uriSplitted = curUri.split(':'); + var self = this; + var defer = libQ.defer(); + var uriSplitted = curUri.split(':'); - var spotifyDefer = self.getAlbumTracks(uriSplitted[2], {limit: 50}); - spotifyDefer.then(function (results) { - var response = { - navigation: { - "prev": { - "uri": 'spotify' - }, - "lists": [ - { - "availableListViews": [ - "list" - ], - "items": [] - } - ] - } - }; + var spotifyDefer = self.getAlbumTracks(uriSplitted[2], { limit: 50 }); + spotifyDefer.then(function (results) { + var response = { + navigation: { + prev: { + uri: 'spotify', + }, + lists: [ + { + availableListViews: ['list'], + items: [], + }, + ], + }, + }; - for (var i in results) { - response.navigation.lists[0].items.push(results[i]); - } - var albumInfo = self.getAlbumInfo(uriSplitted[2]); - albumInfo.then(function (results) { - response.navigation.info = results; - response.navigation.info.uri = curUri; - response.navigation.info.service = 'spop'; - defer.resolve(response); - }) + for (var i in results) { + response.navigation.lists[0].items.push(results[i]); + } + var albumInfo = self.getAlbumInfo(uriSplitted[2]); + albumInfo.then(function (results) { + response.navigation.info = results; + response.navigation.info.uri = curUri; + response.navigation.info.service = 'spop'; + defer.resolve(response); }); + }); - return defer.promise; + return defer.promise; }; - ControllerSpotify.prototype.listWebCategories = function (curUri) { + var self = this; - var self = this; + var defer = libQ.defer(); - var defer = libQ.defer(); + self.spotifyCheckAccessToken().then(function (data) { + var spotifyDefer = self.spotifyApi.getCategories({ limit: 50 }); + spotifyDefer.then( + function (results) { + var response = { + navigation: { + prev: { + uri: 'spotify', + }, + lists: [ + { + availableListViews: ['list', 'grid'], + items: [], + }, + ], + }, + }; - self.spotifyCheckAccessToken() - .then(function (data) { - var spotifyDefer = self.spotifyApi.getCategories({limit: 50}); - spotifyDefer.then(function (results) { - - var response = { - navigation: { - prev: { - uri: 'spotify' - }, - "lists": [ - { - "availableListViews": [ - "list", - "grid" - ], - "items": [] - } - ] - } - }; - - for (var i in results.body.categories.items) { - response.navigation.lists[0].items.push({ - service: 'spop', - type: 'spotify-category', - title: results.body.categories.items[i].name, - albumart: results.body.categories.items[i].icons[0].url, - uri: 'spotify/category/' + results.body.categories.items[i].id - }); - } - defer.resolve(response); - }, function (err) { - self.logger.error('An error occurred while listing Spotify categories ' + err); - defer.reject(''); - }); - }); + for (var i in results.body.categories.items) { + response.navigation.lists[0].items.push({ + service: 'spop', + type: 'spotify-category', + title: results.body.categories.items[i].name, + albumart: results.body.categories.items[i].icons[0].url, + uri: 'spotify/category/' + results.body.categories.items[i].id, + }); + } + defer.resolve(response); + }, + function (err) { + self.logger.error('An error occurred while listing Spotify categories ' + err); + defer.reject(''); + }, + ); + }); - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.listWebCategory = function (curUri) { + var self = this; - var self = this; + var defer = libQ.defer(); - var defer = libQ.defer(); + var uriSplitted = curUri.split('/'); - var uriSplitted = curUri.split('/'); + self.spotifyCheckAccessToken().then(function (data) { + var spotifyDefer = self.spotifyApi.getPlaylistsForCategory(uriSplitted[2], { limit: 50 }); + spotifyDefer.then( + function (results) { + var response = { + navigation: { + prev: { + uri: 'spotify/categories', + }, + lists: [ + { + availableListViews: ['list', 'grid'], + items: [], + }, + ], + }, + }; - self.spotifyCheckAccessToken() - .then(function (data) { - var spotifyDefer = self.spotifyApi.getPlaylistsForCategory(uriSplitted[2], {limit: 50}); - spotifyDefer.then(function (results) { - - var response = { - navigation: { - prev: { - uri: 'spotify/categories' - }, - "lists": [ - { - "availableListViews": [ - "list", - "grid" - ], - "items": [] - } - ] - } - }; - - for (var i in results.body.playlists.items) { - var playlist = results.body.playlists.items[i]; - response.navigation.lists[0].items.push({ - service: 'spop', - type: 'folder', - title: playlist.name, - albumart: self._getAlbumArt(playlist), - uri: playlist.uri - }); - } - defer.resolve(response); - }, function (err) { - self.logger.error('An error occurred while listing Spotify playlist category ' + err); - defer.reject(''); - }); - }); + for (var i in results.body.playlists.items) { + var playlist = results.body.playlists.items[i]; + response.navigation.lists[0].items.push({ + service: 'spop', + type: 'folder', + title: playlist.name, + albumart: self._getAlbumArt(playlist), + uri: playlist.uri, + }); + } + defer.resolve(response); + }, + function (err) { + self.logger.error('An error occurred while listing Spotify playlist category ' + err); + defer.reject(''); + }, + ); + }); - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.listWebArtist = function (curUri) { + var self = this; - var self = this; - - var defer = libQ.defer(); + var defer = libQ.defer(); - var uriSplitted = curUri.split(':'); + var uriSplitted = curUri.split(':'); - var artistId = uriSplitted[2]; + var artistId = uriSplitted[2]; - self.spotifyCheckAccessToken() - .then(function (data) { - var response = { - navigation: { - prev: { - uri: 'spotify' - }, - "lists": [ - { - "availableListViews": [ - "list" - ], - "items": [], - "title": "Top tracks" - }, - { - "availableListViews": [ - "list", - "grid" - ], - "items": [], - "title": "Albums" - }, - { - "availableListViews": [ - "list" - ], - "items": [], - "title": "Related Artists" - } - ] - } - }; - var spotifyDefer = self.listArtistTracks(artistId); - spotifyDefer.then(function (results) { - for (var i in results) { - response.navigation.lists[0].items.push(results[i]); - } - return response; - }) - .then(function (results) { - return self.listArtistAlbums(artistId); - }) - .then(function (results) { - for (var i in results) { - response.navigation.lists[1].items.push(results[i]); - } - return response; - }) - .then(function (results) { - return self.getArtistInfo(artistId); - }) - .then(function (results) { - response.navigation.info = results; - response.navigation.info.uri = curUri; - response.navigation.info.service = 'spop'; - - - return response; - }) - .then(function (results) { - return self.getArtistRelatedArtists(artistId); - }) - .then(function (results) { - for (var i in results) { - response.navigation.lists[2].items.push(results[i]); - } - defer.resolve(response); - return response; - }) - .catch(function (error) { - defer.resolve(response); - }); - }); + self.spotifyCheckAccessToken().then(function (data) { + var response = { + navigation: { + prev: { + uri: 'spotify', + }, + lists: [ + { + availableListViews: ['list'], + items: [], + title: 'Top tracks', + }, + { + availableListViews: ['list', 'grid'], + items: [], + title: 'Albums', + }, + { + availableListViews: ['list'], + items: [], + title: 'Related Artists', + }, + ], + }, + }; + var spotifyDefer = self.listArtistTracks(artistId); + spotifyDefer + .then(function (results) { + for (var i in results) { + response.navigation.lists[0].items.push(results[i]); + } + return response; + }) + .then(function (results) { + return self.listArtistAlbums(artistId); + }) + .then(function (results) { + for (var i in results) { + response.navigation.lists[1].items.push(results[i]); + } + return response; + }) + .then(function (results) { + return self.getArtistInfo(artistId); + }) + .then(function (results) { + response.navigation.info = results; + response.navigation.info.uri = curUri; + response.navigation.info.service = 'spop'; + + return response; + }) + .then(function (results) { + return self.getArtistRelatedArtists(artistId); + }) + .then(function (results) { + for (var i in results) { + response.navigation.lists[2].items.push(results[i]); + } + defer.resolve(response); + return response; + }) + .catch(function (error) { + defer.resolve(response); + }); + }); - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.listArtistTracks = function (id) { + var self = this; - var self = this; - - var defer = libQ.defer(); + var defer = libQ.defer(); - var list = []; + var list = []; - var spotifyDefer = self.getArtistTopTracks(id); - spotifyDefer.then(function (data) { - for (var i in data) { - list.push(data[i]); - } - defer.resolve(list); - }); + var spotifyDefer = self.getArtistTopTracks(id); + spotifyDefer.then(function (data) { + for (var i in data) { + list.push(data[i]); + } + defer.resolve(list); + }); - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.listArtistAlbums = function (id) { + var self = this; - var self = this; - - var defer = libQ.defer(); - - var spotifyDefer = self.spotifyApi.getArtistAlbums(id); - spotifyDefer.then(function (results) { - var response = []; - for (var i in results.body.items) { - var album = results.body.items[i]; - response.push({ - service: 'spop', - type: 'folder', - title: album.name, - albumart: self._getAlbumArt(album), - uri: album.uri, - }); - } - defer.resolve(response); - }) + var defer = libQ.defer(); + var spotifyDefer = self.spotifyApi.getArtistAlbums(id); + spotifyDefer.then(function (results) { + var response = []; + for (var i in results.body.items) { + var album = results.body.items[i]; + response.push({ + service: 'spop', + type: 'folder', + title: album.name, + albumart: self._getAlbumArt(album), + uri: album.uri, + }); + } + defer.resolve(response); + }); - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.getArtistTracks = function (id) { + var self = this; - var self = this; + var defer = libQ.defer(); - var defer = libQ.defer(); + var list = []; - var list = []; - - var spotifyDefer = self.getArtistTopTracks(id); - spotifyDefer.then(function (data) { - for (var i in data) { - list.push(data[i]); - } - return list; + var spotifyDefer = self.getArtistTopTracks(id); + spotifyDefer + .then(function (data) { + for (var i in data) { + list.push(data[i]); + } + return list; }) - .then(function (data) { - var spotifyDefer = self.getArtistAlbumTracks(id); - spotifyDefer.then(function (results) { - var response = data; - for (var i in results) { - response.push(results[i]); - } - defer.resolve(response); - }); - }); + .then(function (data) { + var spotifyDefer = self.getArtistAlbumTracks(id); + spotifyDefer.then(function (results) { + var response = data; + for (var i in results) { + response.push(results[i]); + } + defer.resolve(response); + }); + }); - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.getArtistAlbumTracks = function (id) { + var self = this; - var self = this; - - var defer = libQ.defer(); + var defer = libQ.defer(); - var list = []; + var list = []; - var spotifyDefer = self.spotifyApi.getArtistAlbums(id); - spotifyDefer.then(function (results) { - // var response = data; - var response = []; - return results.body.items.map(function (a) { - return a.id - }); + var spotifyDefer = self.spotifyApi.getArtistAlbums(id); + spotifyDefer + .then(function (results) { + // var response = data; + var response = []; + return results.body.items.map(function (a) { + return a.id; + }); }) - .then(function (albums) { - var spotifyDefer = self.spotifyApi.getAlbums(albums); - spotifyDefer.then(function (data) { - var results = data; - var response = []; - for (var i in results.body.albums) { - var album = results.body.albums[i]; - for (var j in album.tracks.items) { - var track = album.tracks.items[j]; - if (self.isTrackAvailableInCountry(track)) { - response.push({ - service: 'spop', - type: 'song', - name: track.name, - title: track.name, - artist: track.artists[0].name, - album: album.name, - albumart: self._getAlbumArt(album), - uri: track.uri - }); - } - } - } - defer.resolve(response); - }); - }); - + .then(function (albums) { + var spotifyDefer = self.spotifyApi.getAlbums(albums); + spotifyDefer.then(function (data) { + var results = data; + var response = []; + for (var i in results.body.albums) { + var album = results.body.albums[i]; + for (var j in album.tracks.items) { + var track = album.tracks.items[j]; + if (self.isTrackAvailableInCountry(track)) { + response.push({ + service: 'spop', + type: 'song', + name: track.name, + title: track.name, + artist: track.artists[0].name, + album: album.name, + albumart: self._getAlbumArt(album), + uri: track.uri, + }); + } + } + } + defer.resolve(response); + }); + }); - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.getArtistAlbums = function (artistId) { + var self = this; - var self = this; + var defer = libQ.defer(); - var defer = libQ.defer(); - - self.spotifyCheckAccessToken() - .then(function (data) { - var spotifyDefer = self.spotifyApi.getArtistAlbums(artistId); - spotifyDefer.then(function (results) { - var response = []; - for (var i in results.body.items) { - var album = results.body.items[i]; - response.push({ - service: 'spop', - type: 'folder', - title: album.name, - albumart: self._getAlbumArt(album), - uri: album.uri - }); - } - defer.resolve(response); - }); + self.spotifyCheckAccessToken().then(function (data) { + var spotifyDefer = self.spotifyApi.getArtistAlbums(artistId); + spotifyDefer.then(function (results) { + var response = []; + for (var i in results.body.items) { + var album = results.body.items[i]; + response.push({ + service: 'spop', + type: 'folder', + title: album.name, + albumart: self._getAlbumArt(album), + uri: album.uri, }); - return defer.promise; + } + defer.resolve(response); + }); + }); + return defer.promise; }; ControllerSpotify.prototype.getArtistRelatedArtists = function (artistId) { + var self = this; - var self = this; + var defer = libQ.defer(); - var defer = libQ.defer(); + var list = []; - var list = []; - - self.spotifyCheckAccessToken() - .then(function (data) { - var spotifyDefer = self.spotifyApi.getArtistRelatedArtists(artistId); - spotifyDefer.then(function (results) { - for (var i in results.body.artists) { - var albumart = ''; - var artist = results.body.artists[i]; - var albumart = self._getAlbumArt(artist); - var item = { - service: 'spop', - type: 'folder', - title: artist.name, - albumart: albumart, - uri: artist.uri - }; - if (albumart == '') { - item.icon = 'fa fa-user'; - } - list.push(item); - } - defer.resolve(list); - }) - }); + self.spotifyCheckAccessToken().then(function (data) { + var spotifyDefer = self.spotifyApi.getArtistRelatedArtists(artistId); + spotifyDefer.then(function (results) { + for (var i in results.body.artists) { + var albumart = ''; + var artist = results.body.artists[i]; + var albumart = self._getAlbumArt(artist); + var item = { + service: 'spop', + type: 'folder', + title: artist.name, + albumart: albumart, + uri: artist.uri, + }; + if (albumart == '') { + item.icon = 'fa fa-user'; + } + list.push(item); + } + defer.resolve(list); + }); + }); - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.getAlbumTracks = function (id) { - var self = this; - var defer = libQ.defer(); + var self = this; + var defer = libQ.defer(); - self.spotifyCheckAccessToken() - .then(function (data) { - var spotifyDefer = self.spotifyApi.getAlbum(id); - spotifyDefer.then(function (results) { - var response = []; - var album = results.body.name; - var albumart = results.body.images[0].url; - for (var i in results.body.tracks.items) { - var track = results.body.tracks.items[i]; - if (self.isTrackAvailableInCountry(track)) { - response.push({ - service: 'spop', - type: 'song', - title: track.name, - name: track.name, - artist: track.artists[0].name, - album: album, - albumart: albumart, - uri: track.uri, - samplerate: self.getCurrentBitrate(), - bitdepth: '16 bit', - bitrate: '', - codec: 'ogg', - trackType: 'spotify', - duration: Math.trunc(track.duration_ms / 1000) - }); - } - } - defer.resolve(response); - }, function (err) { - self.logger.error('An error occurred while listing Spotify album tracks ' + err); - defer.reject(''); - }); - } - ); + self.spotifyCheckAccessToken().then(function (data) { + var spotifyDefer = self.spotifyApi.getAlbum(id); + spotifyDefer.then( + function (results) { + var response = []; + var album = results.body.name; + var albumart = results.body.images[0].url; + for (var i in results.body.tracks.items) { + var track = results.body.tracks.items[i]; + if (self.isTrackAvailableInCountry(track)) { + response.push({ + service: 'spop', + type: 'song', + title: track.name, + name: track.name, + artist: track.artists[0].name, + album: album, + albumart: albumart, + uri: track.uri, + samplerate: self.getCurrentBitrate(), + bitdepth: '16 bit', + bitrate: '', + codec: 'ogg', + trackType: 'spotify', + duration: Math.trunc(track.duration_ms / 1000), + }); + } + } + defer.resolve(response); + }, + function (err) { + self.logger.error('An error occurred while listing Spotify album tracks ' + err); + defer.reject(''); + }, + ); + }); - return defer.promise; + return defer.promise; }; - ControllerSpotify.prototype.getPlaylistTracks = function (userId, playlistId) { - var defer = libQ.defer(); - var response = []; - - this.spotifyCheckAccessToken().then(() => { - fetchPagedData( - this.spotifyApi, - 'getPlaylistTracks', - { requiredArgs: [playlistId] }, - { - onData: (items) => { - for (var i in items) { - var track = items[i].track; - if (this.isTrackAvailableInCountry(track)) { - var item = { - service: 'spop', - type: 'song', - name: track.name, - title: track.name, - artist: track.artists[0].name, - album: track.album.name, - uri: track.uri, - samplerate: this.getCurrentBitrate(), - bitdepth: '16 bit', - bitrate: '', - codec: 'ogg', - trackType: 'spotify', - albumart: (track.album.hasOwnProperty('images') && track.album.images.length > 0 ? track.album.images[0].url : ''), - duration: Math.trunc(track.duration_ms / 1000) - }; - response.push(item); - } - } - }, - onEnd: () => { - defer.resolve(response); - }, + var defer = libQ.defer(); + var response = []; + + this.spotifyCheckAccessToken().then(() => { + fetchPagedData( + this.spotifyApi, + 'getPlaylistTracks', + { requiredArgs: [playlistId] }, + { + onData: (items) => { + for (var i in items) { + var track = items[i].track; + if (this.isTrackAvailableInCountry(track)) { + var item = { + service: 'spop', + type: 'song', + name: track.name, + title: track.name, + artist: track.artists[0].name, + album: track.album.name, + uri: track.uri, + samplerate: this.getCurrentBitrate(), + bitdepth: '16 bit', + bitrate: '', + codec: 'ogg', + trackType: 'spotify', + albumart: + track.album.hasOwnProperty('images') && track.album.images.length > 0 + ? track.album.images[0].url + : '', + duration: Math.trunc(track.duration_ms / 1000), + }; + response.push(item); } - ).catch((err) => { - this.logger.error('An error occurred while exploding listing Spotify playlist tracks ' + err); - defer.reject(err); - }); + } + }, + onEnd: () => { + defer.resolve(response); + }, + }, + ).catch((err) => { + this.logger.error('An error occurred while exploding listing Spotify playlist tracks ' + err); + defer.reject(err); }); + }); - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.getArtistTopTracks = function (id) { - var self = this; - var defer = libQ.defer(); + var self = this; + var defer = libQ.defer(); - self.spotifyCheckAccessToken() - .then(function (data) { - var spotifyDefer = self.spotifyApi.getArtistTopTracks(id, 'GB'); - spotifyDefer.then(function (results) { - var response = []; - for (var i in results.body.tracks) { - var albumart = ''; - var track = results.body.tracks[i]; - if (track.album.hasOwnProperty('images') && track.album.images.length > 0) { - albumart = track.album.images[0].url; - } - if (self.isTrackAvailableInCountry(track)) { - response.push({ - service: 'spop', - type: 'song', - name: track.name, - title: track.name, - artist: track.artists[0].name, - album: track.album.name, - albumart: albumart, - duration: parseInt(track.duration_ms / 1000), - samplerate: self.getCurrentBitrate(), - bitdepth: '16 bit', - bitrate: '', - codec: 'ogg', - trackType: 'spotify', - uri: track.uri - }); - } - } - defer.resolve(response); - }), function (err) { - self.logger.error('An error occurred while listing Spotify artist tracks ' + err); - defer.reject(''); - } - }); + self.spotifyCheckAccessToken().then(function (data) { + var spotifyDefer = self.spotifyApi.getArtistTopTracks(id, 'GB'); + spotifyDefer.then(function (results) { + var response = []; + for (var i in results.body.tracks) { + var albumart = ''; + var track = results.body.tracks[i]; + if (track.album.hasOwnProperty('images') && track.album.images.length > 0) { + albumart = track.album.images[0].url; + } + if (self.isTrackAvailableInCountry(track)) { + response.push({ + service: 'spop', + type: 'song', + name: track.name, + title: track.name, + artist: track.artists[0].name, + album: track.album.name, + albumart: albumart, + duration: parseInt(track.duration_ms / 1000), + samplerate: self.getCurrentBitrate(), + bitdepth: '16 bit', + bitrate: '', + codec: 'ogg', + trackType: 'spotify', + uri: track.uri, + }); + } + } + defer.resolve(response); + }), + function (err) { + self.logger.error('An error occurred while listing Spotify artist tracks ' + err); + defer.reject(''); + }; + }); - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype.getArtistInfo = function (id) { - var self = this; - var defer = libQ.defer(); + var self = this; + var defer = libQ.defer(); - var info = {}; - self.spotifyCheckAccessToken() - .then(function (data) { - var spotifyDefer = self.spotifyApi.getArtist(id); - spotifyDefer.then(function (results) { - if (results && results.body && results.body.name) { - info.title = results.body.name; - info.albumart = results.body.images[0].url; - info.type = 'artist'; - } - defer.resolve(info); - }), function (err) { - self.logger.info('An error occurred while listing Spotify artist informations ' + err); - defer.resolve(info); - } - }); - - return defer.promise; -} + var info = {}; + self.spotifyCheckAccessToken().then(function (data) { + var spotifyDefer = self.spotifyApi.getArtist(id); + spotifyDefer.then(function (results) { + if (results && results.body && results.body.name) { + info.title = results.body.name; + info.albumart = results.body.images[0].url; + info.type = 'artist'; + } + defer.resolve(info); + }), + function (err) { + self.logger.info('An error occurred while listing Spotify artist informations ' + err); + defer.resolve(info); + }; + }); + + return defer.promise; +}; ControllerSpotify.prototype.getAlbumInfo = function (id) { - var self = this; - var defer = libQ.defer(); - - var info = {}; - self.spotifyCheckAccessToken() - .then(function (data) { - var spotifyDefer = self.spotifyApi.getAlbum(id); - spotifyDefer.then(function (results) { - if (results && results.body && results.body.name) { - info.album = results.body.name; - info.artist = results.body.artists[0].name; - - info.albumart = results.body.images[0].url; - info.type = 'album'; - } - return results.body.artists[0].id; - }).then(function (artist) { - return self.spotifyApi.getArtist(artist); - }).then(function (artistResults) { - if (artistResults && artistResults.body && artistResults.body.name) { - info.artistImage = artistResults.body.images[0].url; - info.artistUri = artistResults.body.uri; - } - defer.resolve(info); - }), function (err) { - self.logger.error('An error occurred while listing Spotify album informations ' + err); - defer.resolve(info); - } - }); + var self = this; + var defer = libQ.defer(); + + var info = {}; + self.spotifyCheckAccessToken().then(function (data) { + var spotifyDefer = self.spotifyApi.getAlbum(id); + spotifyDefer + .then(function (results) { + if (results && results.body && results.body.name) { + info.album = results.body.name; + info.artist = results.body.artists[0].name; + + info.albumart = results.body.images[0].url; + info.type = 'album'; + } + return results.body.artists[0].id; + }) + .then(function (artist) { + return self.spotifyApi.getArtist(artist); + }) + .then(function (artistResults) { + if (artistResults && artistResults.body && artistResults.body.name) { + info.artistImage = artistResults.body.images[0].url; + info.artistUri = artistResults.body.uri; + } + defer.resolve(info); + }), + function (err) { + self.logger.error('An error occurred while listing Spotify album informations ' + err); + defer.resolve(info); + }; + }); - return defer.promise; -} + return defer.promise; +}; ControllerSpotify.prototype.getPlaylistInfo = function (userId, playlistId) { - var self = this; - var defer = libQ.defer(); - - var info = {}; - self.spotifyCheckAccessToken() - .then(function (data) { - var spotifyDefer = self.spotifyApi.getPlaylist(playlistId); - spotifyDefer.then(function (results) { - if (results && results.body && results.body.name) { - info.title = results.body.name; - info.albumart = results.body.images[0].url; - info.type = 'album'; - info.service = 'spop'; - } - defer.resolve(info); - }, function (err) { - defer.resolve(info); - self.logger.error('An error occurred while getting Playlist info: ' + err); - }); - }); + var self = this; + var defer = libQ.defer(); + + var info = {}; + self.spotifyCheckAccessToken().then(function (data) { + var spotifyDefer = self.spotifyApi.getPlaylist(playlistId); + spotifyDefer.then( + function (results) { + if (results && results.body && results.body.name) { + info.title = results.body.name; + info.albumart = results.body.images[0].url; + info.type = 'album'; + info.service = 'spop'; + } + defer.resolve(info); + }, + function (err) { + defer.resolve(info); + self.logger.error('An error occurred while getting Playlist info: ' + err); + }, + ); + }); - return defer.promise; -} + return defer.promise; +}; ControllerSpotify.prototype.getTrack = function (id) { - var defer = libQ.defer(); - - this.spotifyCheckAccessToken().then(() => { - rateLimitedCall(this.spotifyApi, 'getTrack', { args: [id], logger: this.logger }) - .then((results) => { - const track = results.body; - var response = []; - var artist = ''; - var album = ''; - var albumart = ''; - - if (track.artists.length > 0) { - artist = track.artists[0].name; - } - - if (track.hasOwnProperty('album') && track.album.hasOwnProperty('name')) { - album = track.album.name; - } - - if (track.album.hasOwnProperty('images') && track.album.images.length > 0) { - albumart = track.album.images[0].url; - } else { - albumart = ''; - } - - var item = { - uri: track.uri, - service: 'spop', - name: track.name, - artist: artist, - album: album, - type: 'song', - duration: parseInt(track.duration_ms / 1000), - albumart: albumart, - samplerate: this.getCurrentBitrate(), - bitdepth: '16 bit', - bitrate: '', - codec: 'ogg', - trackType: 'spotify' - }; - response.push(item); - this.debugLog('GET TRACK: ' + JSON.stringify(response)); - defer.resolve(response); - }) - .catch((e) => { - defer.reject(e); - }); - }); - return defer.promise; + var defer = libQ.defer(); + + this.spotifyCheckAccessToken().then(() => { + rateLimitedCall(this.spotifyApi, 'getTrack', { args: [id], logger: this.logger }) + .then((results) => { + const track = results.body; + var response = []; + var artist = ''; + var album = ''; + var albumart = ''; + + if (track.artists.length > 0) { + artist = track.artists[0].name; + } + + if (track.hasOwnProperty('album') && track.album.hasOwnProperty('name')) { + album = track.album.name; + } + + if (track.album.hasOwnProperty('images') && track.album.images.length > 0) { + albumart = track.album.images[0].url; + } else { + albumart = ''; + } + + var item = { + uri: track.uri, + service: 'spop', + name: track.name, + artist: artist, + album: album, + type: 'song', + duration: parseInt(track.duration_ms / 1000), + albumart: albumart, + samplerate: this.getCurrentBitrate(), + bitdepth: '16 bit', + bitrate: '', + codec: 'ogg', + trackType: 'spotify', + }; + response.push(item); + this.debugLog('GET TRACK: ' + JSON.stringify(response)); + defer.resolve(response); + }) + .catch((e) => { + defer.reject(e); + }); + }); + return defer.promise; }; // SEARCH FUNCTIONS ControllerSpotify.prototype.search = function (query) { - var self = this; - var defer = libQ.defer(); - - self.spotifyCheckAccessToken() - .then(function (data) { - var spotifyDefer = self.spotifyApi.search(query.value, ['artist', 'album', 'playlist', 'track']); - spotifyDefer.then(function (results) { - var list = []; - // Show artists, albums, playlists then tracks - if (results.body.hasOwnProperty('artists') && results.body.artists.items.length > 0) { - var artistlist = []; - var artists = self._searchArtists(results); - for (var i in artists) { - artistlist.push(artists[i]); - } - list.push({ - type: 'title', - title: 'Spotify ' + self.commandRouter.getI18nString('COMMON.SEARCH_ARTIST_SECTION'), - availableListViews: ["list", "grid"], - items: artistlist - }); - } - if (results.body.hasOwnProperty('albums') && results.body.albums.items.length > 0) { - var albumlist = []; - var albums = self._searchAlbums(results); - for (var i in albums) { - albumlist.push(albums[i]); - } - list.push({ - type: 'title', - title: 'Spotify ' + self.commandRouter.getI18nString('COMMON.SEARCH_ALBUM_SECTION'), - availableListViews: ["list", "grid"], - items: albumlist - }); - } - if (results.body.hasOwnProperty('playlists') && results.body.playlists.items.length > 0) { - var playlistlist = []; - var playlists = self._searchPlaylists(results); - for (var i in playlists) { - playlistlist.push(playlists[i]); - } - list.push({ - type: 'title', - title: 'Spotify ' + self.commandRouter.getI18nString('COMMON.PLAYLISTS'), - availableListViews: ["list", "grid"], - items: playlistlist - }); - } - if (results.body.hasOwnProperty('tracks') && results.body.tracks.items.length > 0) { - var songlist = []; - var tracks = self._searchTracks(results); - for (var i in tracks) { - songlist.push(tracks[i]); - } - list.push({type: 'title', title: 'Spotify ' + self.commandRouter.getI18nString('COMMON.TRACKS'), availableListViews: ["list"], items: songlist}); - } - defer.resolve(list); - }, function (err) { - self.logger.error('An error occurred while searching ' + err); - defer.reject(''); - }); - }); + var self = this; + var defer = libQ.defer(); + + self.spotifyCheckAccessToken().then(function (data) { + var spotifyDefer = self.spotifyApi.search(query.value, ['artist', 'album', 'playlist', 'track']); + spotifyDefer.then( + function (results) { + var list = []; + // Show artists, albums, playlists then tracks + if (results.body.hasOwnProperty('artists') && results.body.artists.items.length > 0) { + var artistlist = []; + var artists = self._searchArtists(results); + for (var i in artists) { + artistlist.push(artists[i]); + } + list.push({ + type: 'title', + title: 'Spotify ' + self.commandRouter.getI18nString('COMMON.SEARCH_ARTIST_SECTION'), + availableListViews: ['list', 'grid'], + items: artistlist, + }); + } + if (results.body.hasOwnProperty('albums') && results.body.albums.items.length > 0) { + var albumlist = []; + var albums = self._searchAlbums(results); + for (var i in albums) { + albumlist.push(albums[i]); + } + list.push({ + type: 'title', + title: 'Spotify ' + self.commandRouter.getI18nString('COMMON.SEARCH_ALBUM_SECTION'), + availableListViews: ['list', 'grid'], + items: albumlist, + }); + } + if (results.body.hasOwnProperty('playlists') && results.body.playlists.items.length > 0) { + var playlistlist = []; + var playlists = self._searchPlaylists(results); + for (var i in playlists) { + playlistlist.push(playlists[i]); + } + list.push({ + type: 'title', + title: 'Spotify ' + self.commandRouter.getI18nString('COMMON.PLAYLISTS'), + availableListViews: ['list', 'grid'], + items: playlistlist, + }); + } + if (results.body.hasOwnProperty('tracks') && results.body.tracks.items.length > 0) { + var songlist = []; + var tracks = self._searchTracks(results); + for (var i in tracks) { + songlist.push(tracks[i]); + } + list.push({ + type: 'title', + title: 'Spotify ' + self.commandRouter.getI18nString('COMMON.TRACKS'), + availableListViews: ['list'], + items: songlist, + }); + } + defer.resolve(list); + }, + function (err) { + self.logger.error('An error occurred while searching ' + err); + defer.reject(''); + }, + ); + }); - return defer.promise; + return defer.promise; }; ControllerSpotify.prototype._searchArtists = function (results) { + var list = []; - var list = []; - - for (var i in results.body.artists.items) { - var albumart = ''; - var artist = results.body.artists.items[i]; - if (artist.hasOwnProperty('images') && artist.images.length > 0) { - albumart = artist.images[0].url; - } - ; - var item = { - service: 'spop', - type: 'folder', - title: artist.name, - albumart: albumart, - uri: artist.uri - }; - if (albumart == '') { - item.icon = 'fa fa-user'; - } - list.push(item); + for (var i in results.body.artists.items) { + var albumart = ''; + var artist = results.body.artists.items[i]; + if (artist.hasOwnProperty('images') && artist.images.length > 0) { + albumart = artist.images[0].url; } + var item = { + service: 'spop', + type: 'folder', + title: artist.name, + albumart: albumart, + uri: artist.uri, + }; + if (albumart == '') { + item.icon = 'fa fa-user'; + } + list.push(item); + } - return list; - + return list; }; ControllerSpotify.prototype._searchAlbums = function (results) { - var list = []; + var list = []; - for (var i in results.body.albums.items) { - var albumart = ''; - var album = results.body.albums.items[i]; - if (album.hasOwnProperty('images') && album.images.length > 0) { - albumart = album.images[0].url; - } - var artist = ''; - if (album.artists && album.artists[0] && album.artists[0].name) { - artist = album.artists[0].name; - } - - list.push({ - service: 'spop', - type: 'folder', - title: album.name, - artist: artist, - albumart: albumart, - uri: album.uri, - }); + for (var i in results.body.albums.items) { + var albumart = ''; + var album = results.body.albums.items[i]; + if (album.hasOwnProperty('images') && album.images.length > 0) { + albumart = album.images[0].url; + } + var artist = ''; + if (album.artists && album.artists[0] && album.artists[0].name) { + artist = album.artists[0].name; } - return list; + list.push({ + service: 'spop', + type: 'folder', + title: album.name, + artist: artist, + albumart: albumart, + uri: album.uri, + }); + } + + return list; }; ControllerSpotify.prototype._searchPlaylists = function (results) { + var list = []; - var list = []; - - for (var i in results.body.playlists.items) { - var albumart = ''; - var playlist = results.body.playlists.items[i]; - if (playlist.hasOwnProperty('images') && playlist.images.length > 0) { - albumart = playlist.images[0].url; - } - ; - list.push({ - service: 'spop', - type: 'folder', - title: playlist.name, - albumart: albumart, - uri: playlist.uri - }); + for (var i in results.body.playlists.items) { + var albumart = ''; + var playlist = results.body.playlists.items[i]; + if (playlist.hasOwnProperty('images') && playlist.images.length > 0) { + albumart = playlist.images[0].url; } + list.push({ + service: 'spop', + type: 'folder', + title: playlist.name, + albumart: albumart, + uri: playlist.uri, + }); + } - return list; + return list; }; ControllerSpotify.prototype._searchTracks = function (results) { + var list = []; - var list = []; - - for (var i in results.body.tracks.items) { - var albumart = ''; - var track = results.body.tracks.items[i]; - if (track.album.hasOwnProperty('images') && track.album.images.length > 0) { - albumart = track.album.images[0].url; - } - ; - list.push({ - service: 'spop', - type: 'song', - title: track.name, - artist: track.artists[0].name, - album: track.album.name, - albumart: albumart, - uri: track.uri - }); + for (var i in results.body.tracks.items) { + var albumart = ''; + var track = results.body.tracks.items[i]; + if (track.album.hasOwnProperty('images') && track.album.images.length > 0) { + albumart = track.album.images[0].url; } + list.push({ + service: 'spop', + type: 'song', + title: track.name, + artist: track.artists[0].name, + album: track.album.name, + albumart: albumart, + uri: track.uri, + }); + } - return list; + return list; }; ControllerSpotify.prototype._searchTracks = function (results) { + var list = []; - var list = []; - - for (var i in results.body.tracks.items) { - var albumart = ''; - var track = results.body.tracks.items[i]; - if (track.album.hasOwnProperty('images') && track.album.images.length > 0) { - albumart = track.album.images[0].url; - } - ; - list.push({ - service: 'spop', - type: 'song', - title: track.name, - artist: track.artists[0].name, - album: track.album.name, - albumart: albumart, - uri: track.uri - }); + for (var i in results.body.tracks.items) { + var albumart = ''; + var track = results.body.tracks.items[i]; + if (track.album.hasOwnProperty('images') && track.album.images.length > 0) { + albumart = track.album.images[0].url; } + list.push({ + service: 'spop', + type: 'song', + title: track.name, + artist: track.artists[0].name, + album: track.album.name, + albumart: albumart, + uri: track.uri, + }); + } - return list; + return list; }; - ControllerSpotify.prototype.searchArtistByName = function (artistName) { - var self = this; - var defer = libQ.defer(); - - self.spotifyApi.search(artistName, ['artist']).then((results)=> { - if (results.body.hasOwnProperty('artists') && results.body.artists.items.length > 0) { - var artistResult = results.body.artists.items[0]; - self.listWebArtist('spotify:artist:' + artistResult.id).then((result)=> { - defer.resolve(result); - }).fail((error)=> { - defer.reject(error); - }); - } else { - defer.reject('No artist found'); - } - }); - return defer.promise; + var self = this; + var defer = libQ.defer(); + + self.spotifyApi.search(artistName, ['artist']).then((results) => { + if (results.body.hasOwnProperty('artists') && results.body.artists.items.length > 0) { + var artistResult = results.body.artists.items[0]; + self + .listWebArtist('spotify:artist:' + artistResult.id) + .then((result) => { + defer.resolve(result); + }) + .fail((error) => { + defer.reject(error); + }); + } else { + defer.reject('No artist found'); + } + }); + return defer.promise; }; ControllerSpotify.prototype.searchAlbumByName = function (albumName) { - var self = this; - var defer = libQ.defer(); - - var spotifyDefer = self.spotifyApi.search(albumName, ['album']); - spotifyDefer.then((results)=> { - if (results.body.hasOwnProperty('albums') && results.body.albums.items.length > 0) { - var albumResult = results.body.albums.items[0]; - self.listWebAlbum('spotify:album:' + albumResult.id).then((result)=> { - defer.resolve(result); - }).fail((error)=> { - defer.reject(error); - }); - } else { - defer.reject('No album found'); - } - }); - return defer.promise; + var self = this; + var defer = libQ.defer(); + + var spotifyDefer = self.spotifyApi.search(albumName, ['album']); + spotifyDefer.then((results) => { + if (results.body.hasOwnProperty('albums') && results.body.albums.items.length > 0) { + var albumResult = results.body.albums.items[0]; + self + .listWebAlbum('spotify:album:' + albumResult.id) + .then((result) => { + defer.resolve(result); + }) + .fail((error) => { + defer.reject(error); + }); + } else { + defer.reject('No album found'); + } + }); + return defer.promise; }; - ControllerSpotify.prototype.goto = function (data) { - var self = this; + var self = this; - if (data.type == 'artist') { - return self.searchArtistByName(data.value); - } else if (data.type == 'album') { - return this.searchAlbumByName(data.value); - } + if (data.type == 'artist') { + return self.searchArtistByName(data.value); + } else if (data.type == 'album') { + return this.searchAlbumByName(data.value); + } }; // PLUGIN FUNCTIONS ControllerSpotify.prototype.debugLog = function (stringToLog) { - var self = this; + var self = this; - if (isDebugMode) { - console.log('SPOTIFY: ' + stringToLog); - } + if (isDebugMode) { + console.log('SPOTIFY: ' + stringToLog); + } }; ControllerSpotify.prototype.isTrackAvailableInCountry = function (currentTrackObj) { - var self = this; - - if (self.userCountry && self.userCountry.length && currentTrackObj && currentTrackObj.available_markets && currentTrackObj.available_markets.length) { - if (currentTrackObj.available_markets.includes(self.userCountry)) { - return true; - } else { - return false; - } + var self = this; + + if ( + self.userCountry && + self.userCountry.length && + currentTrackObj && + currentTrackObj.available_markets && + currentTrackObj.available_markets.length + ) { + if (currentTrackObj.available_markets.includes(self.userCountry)) { + return true; } else { - return true; + return false; } + } else { + return true; + } }; ControllerSpotify.prototype.explodeUri = function (uri) { - var self = this; - - self.debugLog('EXPLODING URI:' + uri); - - var defer = libQ.defer(); - - var uriSplitted; - - var response; - - if (uri.startsWith('spotify/playlists')) { - response = self.getMyPlaylists(); - defer.resolve(response); - } else if (uri.startsWith('spotify:playlist:')) { - uriSplitted = uri.split(':'); - response = self.getPlaylistTracks(uriSplitted[0], uriSplitted[2]); - defer.resolve(response); - } else if (uri.startsWith('spotify:artist:')) { - uriSplitted = uri.split(':'); - response = self.getArtistTracks(uriSplitted[2]); - defer.resolve(response); - } else if (uri.startsWith('spotify:album:')) { - uriSplitted = uri.split(':'); - response = self.getAlbumTracks(uriSplitted[2]); - defer.resolve(response); - } else if (uri.startsWith('spotify:user:')) { - uriSplitted = uri.split(':'); - response = self.getPlaylistTracks(uriSplitted[2], uriSplitted[4]); - defer.resolve(response); - } else if (uri.startsWith('spotify:track:')) { - uriSplitted = uri.split(':'); - response = self.getTrack(uriSplitted[2]); - defer.resolve(response); - } else { - self.logger.info('Bad URI while exploding Spotify URI: ' + uri); - } - - return defer.promise; + var self = this; + + self.debugLog('EXPLODING URI:' + uri); + + var defer = libQ.defer(); + + var uriSplitted; + + var response; + + if (uri.startsWith('spotify/playlists')) { + response = self.getMyPlaylists(); + defer.resolve(response); + } else if (uri.startsWith('spotify:playlist:')) { + uriSplitted = uri.split(':'); + response = self.getPlaylistTracks(uriSplitted[0], uriSplitted[2]); + defer.resolve(response); + } else if (uri.startsWith('spotify:artist:')) { + uriSplitted = uri.split(':'); + response = self.getArtistTracks(uriSplitted[2]); + defer.resolve(response); + } else if (uri.startsWith('spotify:album:')) { + uriSplitted = uri.split(':'); + response = self.getAlbumTracks(uriSplitted[2]); + defer.resolve(response); + } else if (uri.startsWith('spotify:user:')) { + uriSplitted = uri.split(':'); + response = self.getPlaylistTracks(uriSplitted[2], uriSplitted[4]); + defer.resolve(response); + } else if (uri.startsWith('spotify:track:')) { + uriSplitted = uri.split(':'); + response = self.getTrack(uriSplitted[2]); + defer.resolve(response); + } else { + self.logger.info('Bad URI while exploding Spotify URI: ' + uri); + } + + return defer.promise; }; ControllerSpotify.prototype.seekTimerAction = function () { - var self = this; + var self = this; - if (this.state.status === 'play') { - if (seekTimer === undefined) { - seekTimer = setInterval(() => { - this.state.seek = this.state.seek + 1000; - }, 1000); - } - } else { - clearInterval(seekTimer); - seekTimer = undefined; + if (this.state.status === 'play') { + if (seekTimer === undefined) { + seekTimer = setInterval(() => { + this.state.seek = this.state.seek + 1000; + }, 1000); } + } else { + clearInterval(seekTimer); + seekTimer = undefined; + } }; ControllerSpotify.prototype.getLabelForSelect = function (options, key) { - var n = options.length; - for (var i = 0; i < n; i++) { - if (options[i].value === key) { return options[i].label; } + var n = options.length; + for (var i = 0; i < n; i++) { + if (options[i].value === key) { + return options[i].label; } + } - return 'VALUE NOT FOUND BETWEEN SELECT OPTIONS!'; + return 'VALUE NOT FOUND BETWEEN SELECT OPTIONS!'; }; ControllerSpotify.prototype.getSpotifyVolume = function () { - var self = this; - - self.logger.info('Getting Spotify volume'); - superagent.get(spotifyLocalApiEndpointBase + '/player/volume') - .accept('application/json') - .then((results) => { - if (results && results.body && results.body.value) { - self.logger.info('Spotify volume: ' + results.body.value); - currentSpotifyVolume = results.body.value; - } - }) + var self = this; + + self.logger.info('Getting Spotify volume'); + superagent + .get(spotifyLocalApiEndpointBase + '/player/volume') + .accept('application/json') + .then((results) => { + if (results && results.body && results.body.value) { + self.logger.info('Spotify volume: ' + results.body.value); + currentSpotifyVolume = results.body.value; + } + }); }; diff --git a/spotify/install.sh b/spotify/install.sh index fc5497e98..f5af9b6cf 100755 --- a/spotify/install.sh +++ b/spotify/install.sh @@ -5,17 +5,16 @@ echo "Installing Go-librespot" ARCH=$(cat /etc/os-release | grep ^VOLUMIO_ARCH | tr -d 'VOLUMIO_ARCH="') if [ $ARCH = "arm" ]; then - ARCH="armv6_rpi" + ARCH="armv6_rpi" elif [ $ARCH = "armv7" ]; then - ARCH="armv6" -elif [ $ARCH = "amd64" ] || [ $ARCH = "x86_64" ] || [ $ARCH = "x64" ]; then - ARCH="x86_64" -elif [ $ARCH = "i386" ] || [ $ARCH = "i686" ] || [ $ARCH = "x86" ]; then - echo "Platform not supported" - exit 1 + ARCH="armv6" +elif [ $ARCH = "amd64" ] || [ $ARCH = "x86_64" ] || [ $ARCH = "x64" ]; then + ARCH="x86_64" +elif [ $ARCH = "i386" ] || [ $ARCH = "i686" ] || [ $ARCH = "x86" ]; then + echo "Platform not supported" + exit 1 fi - echo "Checking old vollibrespot installs" killall vollibrespot @@ -43,7 +42,6 @@ if [ -d $VOLSPOTCONNECT2_PATH ]; then echo "volspotconnect2 plugin cleared" fi - DAEMON_BASE_URL=https://github.com/devgianlu/go-librespot/releases/download/v VERSION=0.0.10 DAEMON_ARCHIVE=go-librespot_linux_$ARCH.tar.gz @@ -69,11 +67,10 @@ DAEMON_DATA_PATH=/data/go-librespot/ [ -d $DAEMON_DATA_PATH ] || mkdir $DAEMON_DATA_PATH echo 'Librespot-go daemon starting...' -/usr/bin/go-librespot -config_path /tmp/go-librespot-config.yml -credentials_path /data/configuration/music_service/spop/spotifycredentials.json" > /bin/start-go-liberspot.sh +/usr/bin/go-librespot -config_path /tmp/go-librespot-config.yml -credentials_path /data/configuration/music_service/spop/spotifycredentials.json" >/bin/start-go-liberspot.sh chmod a+x /bin/start-go-liberspot.sh - echo "[Unit] Description = go-librespot Daemon After = volumio.service @@ -88,7 +85,7 @@ SyslogIdentifier=go-librespot User=volumio Group=volumio [Install] -WantedBy=multi-user.target" > /lib/systemd/system/go-librespot-daemon.service +WantedBy=multi-user.target" >/lib/systemd/system/go-librespot-daemon.service systemctl daemon-reload diff --git a/spotify/package.json b/spotify/package.json index 6b2ca695a..1be1598c9 100755 --- a/spotify/package.json +++ b/spotify/package.json @@ -1,45 +1,45 @@ { - "name": "spop", - "version": "4.0.4", - "description": "Spotify plugin for Volumio3", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "Ashthespy, Balbuze, Skirkwood", - "license": "ISC", - "vollibrespot": { - "version": "0.2.5" - }, - "volumio_info": { - "prettyName": "Spotify", - "icon": "fa-spotify", - "plugin_type": "music_service", - "boot_priority": 10, - "os": [ - "buster" - ], - "details": "Spotify plugin for Volumio3", - "changelog": "", - "architectures": [ - "amd64", - "armhf", - "i386" - ] - }, - "dependencies": { - "fs-extra": "^0.28.0", - "kew": "^0.7.0", - "node-cache": "^4.1.1", - "socket.io-client": "2.1.1", - "spotify-web-api-node": "^5.0.2", - "superagent": "^8.1.2", - "v-conf": "^1.4.2", - "ws": "^8.13.0" - }, - "engines": { - "volumio": ">=3", - "node": ">=14.15.4" - }, - "repository": "https://github.com/volumio/volumio-plugins-sources" + "name": "spop", + "version": "4.0.4", + "description": "Spotify plugin for Volumio3", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Ashthespy, Balbuze, Skirkwood", + "license": "ISC", + "vollibrespot": { + "version": "0.2.5" + }, + "volumio_info": { + "prettyName": "Spotify", + "icon": "fa-spotify", + "plugin_type": "music_service", + "boot_priority": 10, + "os": [ + "buster" + ], + "details": "Spotify plugin for Volumio3", + "changelog": "", + "architectures": [ + "amd64", + "armhf", + "i386" + ] + }, + "dependencies": { + "fs-extra": "^0.28.0", + "kew": "^0.7.0", + "node-cache": "^4.1.1", + "socket.io-client": "2.1.1", + "spotify-web-api-node": "^5.0.2", + "superagent": "^8.1.2", + "v-conf": "^1.4.2", + "ws": "^8.13.0" + }, + "engines": { + "volumio": ">=3", + "node": ">=14.15.4" + }, + "repository": "https://github.com/volumio/volumio-plugins-sources" } diff --git a/spotify/utils/extendedSpotifyApi/fetchPagedData.js b/spotify/utils/extendedSpotifyApi/fetchPagedData.js index a07cdc47f..c84ddf5b9 100644 --- a/spotify/utils/extendedSpotifyApi/fetchPagedData.js +++ b/spotify/utils/extendedSpotifyApi/fetchPagedData.js @@ -4,7 +4,7 @@ async function fetchPagedData( api, method, { requiredArgs = [], options, paginationType = 'offset' }, - { getItems = (d) => d.body?.items || [], onData, onEnd } + { getItems = (d) => d.body?.items || [], onData, onEnd }, ) { let offset = 0; let after = undefined; diff --git a/spotify/utils/extendedSpotifyApi/rateLimitedCall.js b/spotify/utils/extendedSpotifyApi/rateLimitedCall.js index 1d716c9b1..f991735dd 100644 --- a/spotify/utils/extendedSpotifyApi/rateLimitedCall.js +++ b/spotify/utils/extendedSpotifyApi/rateLimitedCall.js @@ -40,7 +40,7 @@ function call(api, method, { logger, args, attempt } = {}) { } catch (e) { if (e.statusCode === 429) { logger.warn( - `Spotify API method ${method} failed due to "Too many requests". Stop all API requests for ${COOLDOWN_PERIOD} milliseconds.` + `Spotify API method ${method} failed due to "Too many requests". Stop all API requests for ${COOLDOWN_PERIOD} milliseconds.`, ); cooldown(); call(api, method, { logger, args, attempt: attempt + 1 }) diff --git a/spotify/wsclient.js b/spotify/wsclient.js index 25ecd7e51..32abf2085 100644 --- a/spotify/wsclient.js +++ b/spotify/wsclient.js @@ -3,14 +3,14 @@ var websocket = require('ws'); var ws = new websocket('ws://localhost:9876/events'); -ws.on('error', function(error){ - console.error(error); +ws.on('error', function (error) { + console.error(error); }); ws.on('open', function open() { - ws.send('something'); + ws.send('something'); }); ws.on('message', function message(data) { - console.log('received: %s', data); -}); \ No newline at end of file + console.log('received: %s', data); +}); From cf1b5b542e47aefe59806c6155a7e1c03e2ae4fb Mon Sep 17 00:00:00 2001 From: Phil Tsaryk Date: Wed, 31 Jan 2024 21:34:40 +0100 Subject: [PATCH 3/6] fix: eslint --fix --- spotify/apiclient.js | 4 +- spotify/index.js | 726 +++++++++++++++++++++---------------------- spotify/wsclient.js | 4 +- 3 files changed, 367 insertions(+), 367 deletions(-) diff --git a/spotify/apiclient.js b/spotify/apiclient.js index f47109f9d..2a6bd9275 100644 --- a/spotify/apiclient.js +++ b/spotify/apiclient.js @@ -1,6 +1,6 @@ -var superagent = require('superagent'); +let superagent = require('superagent'); -var apiEndpoint = 'http://127.0.0.1:9879'; +let apiEndpoint = 'http://127.0.0.1:9879'; superagent .post(apiEndpoint + '/player/volume') diff --git a/spotify/index.js b/spotify/index.js index 588af144e..4ea7d5d68 100644 --- a/spotify/index.js +++ b/spotify/index.js @@ -1,55 +1,55 @@ 'use strict'; -var libQ = require('kew'); -var fs = require('fs-extra'); -var config = new (require('v-conf'))(); -var superagent = require('superagent'); +let libQ = require('kew'); +let fs = require('fs-extra'); +let config = new (require('v-conf'))(); +let superagent = require('superagent'); var os = require('os'); -var websocket = require('ws'); -var path = require('path'); -var SpotifyWebApi = require('spotify-web-api-node'); -var io = require('socket.io-client'); -var exec = require('child_process').exec; -var execSync = require('child_process').execSync; -var NodeCache = require('node-cache'); +let websocket = require('ws'); +let path = require('path'); +let SpotifyWebApi = require('spotify-web-api-node'); +let io = require('socket.io-client'); +let exec = require('child_process').exec; +let execSync = require('child_process').execSync; +let NodeCache = require('node-cache'); var os = require('os'); -var { fetchPagedData, rateLimitedCall } = require('./utils/extendedSpotifyApi'); - -var configFileDestinationPath = '/tmp/go-librespot-config.yml'; -var credentialsPath = '/data/configuration/music_service/spop/spotifycredentials.json'; -var spotifyDaemonPort = '9879'; -var spotifyLocalApiEndpointBase = 'http://127.0.0.1:' + spotifyDaemonPort; -var stateSocket = undefined; - -var selectedBitrate; -var loggedInUsername; -var loggedInUserId; -var userCountry; -var seekTimer; -var restartTimeout; -var wsConnectionStatus = 'started'; +let { fetchPagedData, rateLimitedCall } = require('./utils/extendedSpotifyApi'); + +let configFileDestinationPath = '/tmp/go-librespot-config.yml'; +let credentialsPath = '/data/configuration/music_service/spop/spotifycredentials.json'; +let spotifyDaemonPort = '9879'; +let spotifyLocalApiEndpointBase = 'http://127.0.0.1:' + spotifyDaemonPort; +let stateSocket = undefined; + +let selectedBitrate; +let loggedInUsername; +let loggedInUserId; +let userCountry; +let seekTimer; +let restartTimeout; +let wsConnectionStatus = 'started'; // State management -var ws; -var currentVolumioState; -var currentSpotifyVolume; -var currentVolumioVolume; -var isInVolatileMode = false; -var ignoreStopEvent = false; +let ws; +let currentVolumioState; +let currentSpotifyVolume; +let currentVolumioVolume; +let isInVolatileMode = false; +let ignoreStopEvent = false; // Volume limiter -var deltaVolumeTreshold = 2; -var volumeDebounce; +let deltaVolumeTreshold = 2; +let volumeDebounce; // Debug -var isDebugMode = true; +let isDebugMode = true; // Define the ControllerSpotify class module.exports = ControllerSpotify; function ControllerSpotify(context) { // This fixed variable will let us refer to 'this' object at deeper scopes - var self = this; + let self = this; this.context = context; this.commandRouter = this.context.coreCommand; @@ -59,8 +59,8 @@ function ControllerSpotify(context) { } ControllerSpotify.prototype.onVolumioStart = function () { - var self = this; - var configFile = this.commandRouter.pluginManager.getConfigurationFile(this.context, 'config.json'); + let self = this; + let configFile = this.commandRouter.pluginManager.getConfigurationFile(this.context, 'config.json'); this.config = new (require('v-conf'))(); this.config.loadFile(configFile); @@ -72,8 +72,8 @@ ControllerSpotify.prototype.getConfigurationFiles = function () { }; ControllerSpotify.prototype.onStop = function () { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); self.goLibrespotDaemonWsConnection('stop'); self.stopLibrespotDaemon(); @@ -85,8 +85,8 @@ ControllerSpotify.prototype.onStop = function () { }; ControllerSpotify.prototype.onStart = function () { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); self.loadI18n(); self.browseCache = new NodeCache({ stdTTL: 3600, checkperiod: 120 }); @@ -97,10 +97,10 @@ ControllerSpotify.prototype.onStart = function () { }; ControllerSpotify.prototype.getUIConfig = function () { - var defer = libQ.defer(); - var self = this; + let defer = libQ.defer(); + let self = this; - var lang_code = self.commandRouter.sharedVars.get('language_code'); + let lang_code = self.commandRouter.sharedVars.get('language_code'); self.commandRouter .i18nJson( @@ -109,23 +109,23 @@ ControllerSpotify.prototype.getUIConfig = function () { __dirname + '/UIConfig.json', ) .then(function (uiconf) { - var credentials_type = self.config.get('credentials_type', 'zeroconf'); + let credentials_type = self.config.get('credentials_type', 'zeroconf'); if (self.loggedInUserId !== undefined && credentials_type === 'spotify_token') { uiconf.sections[1].content[0].hidden = true; uiconf.sections[1].content[1].hidden = false; } - var bitrateNumber = self.config.get('bitrate_number', 320); + let bitrateNumber = self.config.get('bitrate_number', 320); uiconf.sections[2].content[0].value.value = bitrateNumber; uiconf.sections[2].content[0].value.label = self.getLabelForSelect( uiconf.sections[2].content[0].options, bitrateNumber, ); - var normalisationPregainValue = self.config.get('normalisation_pregain', '1.0'); + let normalisationPregainValue = self.config.get('normalisation_pregain', '1.0'); uiconf.sections[2].content[2].value.value = normalisationPregainValue; uiconf.sections[2].content[2].value.label = normalisationPregainValue; - var icon = self.config.get('icon', 'avr'); + let icon = self.config.get('icon', 'avr'); uiconf.sections[2].content[3].value.value = icon; uiconf.sections[2].content[3].value.label = self.getLabelForSelect(uiconf.sections[2].content[3].options, icon); @@ -140,8 +140,8 @@ ControllerSpotify.prototype.getUIConfig = function () { }; ControllerSpotify.prototype.getAdditionalConf = function (type, controller, data, def) { - var self = this; - var setting = self.commandRouter.executeOnPlugin(type, controller, 'getConfigParam', data); + let self = this; + let setting = self.commandRouter.executeOnPlugin(type, controller, 'getConfigParam', data); if (setting == undefined) { setting = def; @@ -152,7 +152,7 @@ ControllerSpotify.prototype.getAdditionalConf = function (type, controller, data // Controls ControllerSpotify.prototype.goLibrespotDaemonWsConnection = function (action) { - var self = this; + let self = this; if (action === 'start') { wsConnectionStatus = 'started'; @@ -177,7 +177,7 @@ ControllerSpotify.prototype.goLibrespotDaemonWsConnection = function (action) { }; ControllerSpotify.prototype.initializeWsConnection = function () { - var self = this; + let self = this; self.logger.info('Initializing connection to go-librespot Websocket'); @@ -205,7 +205,7 @@ ControllerSpotify.prototype.initializeWsConnection = function () { }; ControllerSpotify.prototype.initializeSpotifyControls = function () { - var self = this; + let self = this; self.resetSpotifyState(); self.startSocketStateListener(); @@ -213,7 +213,7 @@ ControllerSpotify.prototype.initializeSpotifyControls = function () { }; ControllerSpotify.prototype.resetSpotifyState = function () { - var self = this; + let self = this; this.state = { status: 'stop', @@ -239,9 +239,9 @@ ControllerSpotify.prototype.resetSpotifyState = function () { }; ControllerSpotify.prototype.parseEventState = function (event) { - var self = this; + let self = this; - var pushStateforEvent = false; + let pushStateforEvent = false; // create a switch case which handles types of events // and updates the state accordingly @@ -324,7 +324,7 @@ ControllerSpotify.prototype.parseEventState = function (event) { }; ControllerSpotify.prototype.identifyPlaybackMode = function (data) { - var self = this; + let self = this; // This functions checks if Spotify is playing in volatile mode or in Volumio mode (playback started from Volumio UI) // play_origin = 'go-librespot' means that Spotify is playing in Volumio mode @@ -345,7 +345,7 @@ ControllerSpotify.prototype.identifyPlaybackMode = function (data) { }; ControllerSpotify.prototype.initializeSpotifyPlaybackInVolatileMode = function () { - var self = this; + let self = this; self.logger.info('Spotify is playing in volatile mode'); ignoreStopEvent = true; @@ -362,7 +362,7 @@ ControllerSpotify.prototype.initializeSpotifyPlaybackInVolatileMode = function ( }; ControllerSpotify.prototype.parseDuration = function (spotifyDuration) { - var self = this; + let self = this; try { return parseInt(spotifyDuration / 1000); @@ -372,17 +372,17 @@ ControllerSpotify.prototype.parseDuration = function (spotifyDuration) { }; ControllerSpotify.prototype.getCurrentBitrate = function () { - var self = this; + let self = this; return self.selectedBitrate + ' kbps'; }; ControllerSpotify.prototype.parseArtists = function (spotifyArtists) { - var self = this; + let self = this; - var artist = ''; + let artist = ''; if (spotifyArtists.length > 0) { - for (var i in spotifyArtists) { + for (let i in spotifyArtists) { if (!artist.length) { artist = spotifyArtists[i]; } else { @@ -396,8 +396,8 @@ ControllerSpotify.prototype.parseArtists = function (spotifyArtists) { }; ControllerSpotify.prototype.libRespotGoUnsetVolatile = function () { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); self.debugLog('UNSET VOLATILE'); self.debugLog(JSON.stringify(currentVolumioState)); @@ -414,7 +414,7 @@ ControllerSpotify.prototype.libRespotGoUnsetVolatile = function () { }; ControllerSpotify.prototype.getState = function () { - var self = this; + let self = this; self.debugLog('GET STATE SPOTIFY'); self.debugLog(JSON.stringify(self.state)); @@ -423,7 +423,7 @@ ControllerSpotify.prototype.getState = function () { // Announce updated Spop state ControllerSpotify.prototype.pushState = function (state) { - var self = this; + let self = this; self.state.bitrate = self.getCurrentBitrate(); self.debugLog('PUSH STATE SPOTIFY'); @@ -477,7 +477,7 @@ ControllerSpotify.prototype.play = function () { ControllerSpotify.prototype.stop = function () { this.logger.info('Spotify Stop'); - var defer = libQ.defer(); + let defer = libQ.defer(); this.debugLog('SPOTIFY STOP'); this.debugLog(JSON.stringify(currentVolumioState)); @@ -533,7 +533,7 @@ ControllerSpotify.prototype.repeat = function (value, repeatSingle) { // Volume events ControllerSpotify.prototype.onSpotifyVolumeChange = function (volume) { - var self = this; + let self = this; self.debugLog('RECEIVED SPOTIFY VOLUME ' + volume); if (volume !== currentVolumioVolume) { @@ -545,7 +545,7 @@ ControllerSpotify.prototype.onSpotifyVolumeChange = function (volume) { }; ControllerSpotify.prototype.onVolumioVolumeChange = function (volume) { - var self = this; + let self = this; self.debugLog('RECEIVED VOLUMIO VOLUME ' + volume); if (volume !== currentSpotifyVolume && self.checkSpotifyAndVolumioDeltaVolumeIsEnough(currentSpotifyVolume, volume)) { @@ -557,7 +557,7 @@ ControllerSpotify.prototype.onVolumioVolumeChange = function (volume) { }; ControllerSpotify.prototype.setSpotifyDaemonVolume = function (volume) { - var self = this; + let self = this; // Volume limiter if (volumeDebounce) { @@ -570,7 +570,7 @@ ControllerSpotify.prototype.setSpotifyDaemonVolume = function (volume) { }; ControllerSpotify.prototype.checkSpotifyAndVolumioDeltaVolumeIsEnough = function (spotifyVolume, volumioVolume) { - var self = this; + let self = this; self.debugLog('SPOTIFY VOLUME ' + spotifyVolume); self.debugLog('VOLUMIO VOLUME ' + volumioVolume); @@ -578,7 +578,7 @@ ControllerSpotify.prototype.checkSpotifyAndVolumioDeltaVolumeIsEnough = function return self.alignSpotifyVolumeToVolumioVolume(); } try { - var isDeltaVolumeEnough = Math.abs(parseInt(spotifyVolume) - parseInt(volumioVolume)) >= deltaVolumeTreshold; + let isDeltaVolumeEnough = Math.abs(parseInt(spotifyVolume) - parseInt(volumioVolume)) >= deltaVolumeTreshold; self.debugLog('DELTA VOLUME ENOUGH: ' + isDeltaVolumeEnough); return isDeltaVolumeEnough; } catch (e) { @@ -587,7 +587,7 @@ ControllerSpotify.prototype.checkSpotifyAndVolumioDeltaVolumeIsEnough = function }; ControllerSpotify.prototype.alignSpotifyVolumeToVolumioVolume = function () { - var self = this; + let self = this; self.logger.info('Aligning Spotify Volume to Volumio Volume'); @@ -608,7 +608,7 @@ ControllerSpotify.prototype.alignSpotifyVolumeToVolumioVolume = function () { }; ControllerSpotify.prototype.clearAddPlayTrack = function (track) { - var self = this; + let self = this; self.commandRouter.pushConsoleMessage('[' + Date.now() + '] ' + 'ControllerSpotify::clearAddPlayTrack'); self.resetSpotifyState(); @@ -616,7 +616,7 @@ ControllerSpotify.prototype.clearAddPlayTrack = function (track) { }; ControllerSpotify.prototype.startSocketStateListener = function () { - var self = this; + let self = this; if (self.stateSocket) { self.stateSocket.off(); @@ -631,7 +631,7 @@ ControllerSpotify.prototype.startSocketStateListener = function () { self.stateSocket.on('pushState', function (data) { currentVolumioState = data; if (data && data.volume && !data.disableVolumeControl) { - var currentVolume = data.volume; + let currentVolume = data.volume; if (data.mute === true) { currentVolume = 0; } @@ -641,7 +641,7 @@ ControllerSpotify.prototype.startSocketStateListener = function () { }; ControllerSpotify.prototype.stopSocketStateListener = function () { - var self = this; + let self = this; if (self.stateSocket) { self.stateSocket.off(); @@ -652,8 +652,8 @@ ControllerSpotify.prototype.stopSocketStateListener = function () { // DAEMON MANAGEMENT ControllerSpotify.prototype.initializeLibrespotDaemon = function () { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); this.selectedBitrate = self.config.get('bitrate_number', '320').toString(); @@ -678,8 +678,8 @@ ControllerSpotify.prototype.initializeLibrespotDaemon = function () { }; ControllerSpotify.prototype.startLibrespotDaemon = function () { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); exec('/usr/bin/sudo systemctl restart go-librespot-daemon.service', function (error, stdout, stderr) { if (error) { @@ -696,8 +696,8 @@ ControllerSpotify.prototype.startLibrespotDaemon = function () { }; ControllerSpotify.prototype.stopLibrespotDaemon = function () { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); exec('/usr/bin/sudo systemctl stop go-librespot-daemon.service', function (error, stdout, stderr) { if (error) { @@ -714,8 +714,8 @@ ControllerSpotify.prototype.stopLibrespotDaemon = function () { }; ControllerSpotify.prototype.createConfigFile = function () { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); this.logger.info('Creating Spotify config file'); @@ -725,26 +725,26 @@ ControllerSpotify.prototype.createConfigFile = function () { this.logger.error('Failed to read template file: ' + e); } - var devicename = this.commandRouter.sharedVars.get('system.name'); - var selectedBitrate = self.config.get('bitrate_number', '320').toString(); - var icon = self.config.get('icon', 'avr'); - var externalVolume = true; - var mixerType = self.getAdditionalConf('audio_interface', 'alsa_controller', 'mixer_type', 'None'); + let devicename = this.commandRouter.sharedVars.get('system.name'); + let selectedBitrate = self.config.get('bitrate_number', '320').toString(); + let icon = self.config.get('icon', 'avr'); + let externalVolume = true; + let mixerType = self.getAdditionalConf('audio_interface', 'alsa_controller', 'mixer_type', 'None'); if (mixerType === 'None') { externalVolume = false; } - var normalisationPregain = self.config.get('normalisation_pregain', '1.0'); + let normalisationPregain = self.config.get('normalisation_pregain', '1.0'); - var conf = template + let conf = template .replace('${device_name}', devicename) .replace('${bitrate_number}', selectedBitrate) .replace('${device_type}', icon) .replace('${external_volume}', externalVolume) .replace('${normalisation_pregain}', normalisationPregain); - var credentials_type = self.config.get('credentials_type', 'zeroconf'); - var logged_user_id = self.config.get('logged_user_id', ''); - var access_token = self.config.get('access_token', ''); + let credentials_type = self.config.get('credentials_type', 'zeroconf'); + let logged_user_id = self.config.get('logged_user_id', ''); + let access_token = self.config.get('access_token', ''); if (credentials_type === 'spotify_token' && logged_user_id !== '' && access_token !== '') { conf += 'credentials: ' + os.EOL; @@ -770,7 +770,7 @@ ControllerSpotify.prototype.createConfigFile = function () { }; ControllerSpotify.prototype.isOauthLoginAlreadyConfiguredOnDaemon = function () { - var self = this; + let self = this; try { var credentialsFile = fs.readFileSync(credentialsPath, { encoding: 'utf8' }).toString(); @@ -786,10 +786,10 @@ ControllerSpotify.prototype.isOauthLoginAlreadyConfiguredOnDaemon = function () }; ControllerSpotify.prototype.saveGoLibrespotSettings = function (data, avoidBroadcastUiConfig) { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); - var broadcastUiConfig = true; + let broadcastUiConfig = true; if (avoidBroadcastUiConfig === true) { broadcastUiConfig = false; } @@ -817,10 +817,10 @@ ControllerSpotify.prototype.saveGoLibrespotSettings = function (data, avoidBroad // OAUTH ControllerSpotify.prototype.refreshAccessToken = function () { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); - var refreshToken = self.config.get('refresh_token', 'none'); + let refreshToken = self.config.get('refresh_token', 'none'); if (refreshToken !== 'none' && refreshToken !== null && refreshToken !== undefined) { superagent .post('https://oauth-performer.dfs.volumio.org/spotify/accessToken') @@ -841,12 +841,12 @@ ControllerSpotify.prototype.refreshAccessToken = function () { }; ControllerSpotify.prototype.spotifyClientCredentialsGrant = function () { - var self = this; - var defer = libQ.defer(); - var d = new Date(); - var now = d.getTime(); + let self = this; + let defer = libQ.defer(); + let d = new Date(); + let now = d.getTime(); - var refreshToken = self.config.get('refresh_token', 'none'); + let refreshToken = self.config.get('refresh_token', 'none'); if (refreshToken !== 'none' && refreshToken !== null && refreshToken !== undefined) { self.spotifyApi.setRefreshToken(refreshToken); self.refreshAccessToken().then( @@ -875,7 +875,7 @@ ControllerSpotify.prototype.spotifyClientCredentialsGrant = function () { }; ControllerSpotify.prototype.oauthLogin = function (data) { - var self = this; + let self = this; self.logger.info('Executing Spotify Oauth Login'); @@ -889,7 +889,7 @@ ControllerSpotify.prototype.oauthLogin = function (data) { self.config.set('credentials_type', 'spotify_token'); self.initializeLibrespotDaemon(); self.initializeSpotifyBrowsingFacility(); - var config = self.getUIConfig(); + let config = self.getUIConfig(); config.then(function (conf) { self.commandRouter.broadcastMessage('pushUiConfig', conf); self.commandRouter.broadcastMessage('closeAllModals', ''); @@ -905,8 +905,8 @@ ControllerSpotify.prototype.oauthLogin = function (data) { }; ControllerSpotify.prototype.externalOauthLogin = function (data) { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); if (data && data.refresh_token) { self.logger.info('Saving Spotify Refresh Token'); @@ -923,9 +923,9 @@ ControllerSpotify.prototype.externalOauthLogin = function (data) { }; ControllerSpotify.prototype.logout = function (avoidBroadcastUiConfig) { - var self = this; + let self = this; - var broadcastUiConfig = true; + let broadcastUiConfig = true; if (avoidBroadcastUiConfig === true) { broadcastUiConfig = false; } @@ -943,10 +943,10 @@ ControllerSpotify.prototype.logout = function (avoidBroadcastUiConfig) { }; ControllerSpotify.prototype.pushUiConfig = function (broadcastUiConfig) { - var self = this; + let self = this; setTimeout(() => { - var config = self.getUIConfig(); + let config = self.getUIConfig(); config.then((conf) => { if (broadcastUiConfig) { self.commandRouter.broadcastMessage('pushUiConfig', conf); @@ -956,7 +956,7 @@ ControllerSpotify.prototype.pushUiConfig = function (broadcastUiConfig) { }; ControllerSpotify.prototype.resetSpotifyCredentials = function () { - var self = this; + let self = this; self.config.set('logged_user_id', ''); self.config.set('access_token', ''); @@ -973,7 +973,7 @@ ControllerSpotify.prototype.resetSpotifyCredentials = function () { }; ControllerSpotify.prototype.deleteCredentialsFile = function () { - var self = this; + let self = this; self.logger.info('Deleting Spotify credentials File'); try { @@ -984,9 +984,9 @@ ControllerSpotify.prototype.deleteCredentialsFile = function () { }; ControllerSpotify.prototype.spotifyApiConnect = function () { - var self = this; - var defer = libQ.defer(); - var d = new Date(); + let self = this; + let defer = libQ.defer(); + let d = new Date(); self.spotifyApi = new SpotifyWebApi(); @@ -1014,10 +1014,10 @@ ControllerSpotify.prototype.spotifyApiConnect = function () { }; ControllerSpotify.prototype.refreshAccessToken = function () { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); - var refreshToken = self.config.get('refresh_token', 'none'); + let refreshToken = self.config.get('refresh_token', 'none'); if (refreshToken !== 'none' && refreshToken !== null && refreshToken !== undefined) { superagent .post('https://oauth-performer.dfs.volumio.org/spotify/accessToken') @@ -1038,10 +1038,10 @@ ControllerSpotify.prototype.refreshAccessToken = function () { }; ControllerSpotify.prototype.spotifyCheckAccessToken = function () { - var self = this; - var defer = libQ.defer(); - var d = new Date(); - var now = d.getTime(); + let self = this; + let defer = libQ.defer(); + let d = new Date(); + let now = d.getTime(); if (self.spotifyAccessTokenExpiration < now) { self.refreshAccessToken().then(function (data) { @@ -1059,9 +1059,9 @@ ControllerSpotify.prototype.spotifyCheckAccessToken = function () { }; ControllerSpotify.prototype.initializeSpotifyBrowsingFacility = function () { - var self = this; + let self = this; - var refreshToken = self.config.get('refresh_token', 'none'); + let refreshToken = self.config.get('refresh_token', 'none'); if (refreshToken !== 'none' && refreshToken !== null && refreshToken !== undefined) { self .spotifyApiConnect() @@ -1077,8 +1077,8 @@ ControllerSpotify.prototype.initializeSpotifyBrowsingFacility = function () { }; ControllerSpotify.prototype.getUserInformations = function () { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); self.spotifyApi.getMe().then( function (data) { @@ -1103,7 +1103,7 @@ ControllerSpotify.prototype.getUserInformations = function () { // CACHE ControllerSpotify.prototype.flushCache = function () { - var self = this; + let self = this; self.browseCache.flushAll(); }; @@ -1111,7 +1111,7 @@ ControllerSpotify.prototype.flushCache = function () { // ALBUMART ControllerSpotify.prototype._getAlbumArt = function (item) { - var albumart = ''; + let albumart = ''; if (item.hasOwnProperty('images') && item.images.length > 0) { albumart = item.images[0].url; } @@ -1119,13 +1119,13 @@ ControllerSpotify.prototype._getAlbumArt = function (item) { }; ControllerSpotify.prototype.getAlbumArt = function (data, path) { - var artist, album; + let artist, album; if (data != undefined && data.path != undefined) { path = data.path; } - var web; + let web; if (data != undefined && data.artist != undefined) { artist = data.artist; @@ -1135,7 +1135,7 @@ ControllerSpotify.prototype.getAlbumArt = function (data, path) { web = '?web=' + encodeURIComponent(artist) + '/' + encodeURIComponent(album) + '/large'; } - var url = '/albumart'; + let url = '/albumart'; if (web != undefined) url = url + web; @@ -1150,10 +1150,10 @@ ControllerSpotify.prototype.getAlbumArt = function (data, path) { // TRANSLATIONS ControllerSpotify.prototype.loadI18n = function () { - var self = this; + let self = this; try { - var language_code = this.commandRouter.sharedVars.get('language_code'); + let language_code = this.commandRouter.sharedVars.get('language_code'); self.i18n = fs.readJsonSync(__dirname + '/i18n/strings_' + language_code + '.json'); } catch (e) { self.i18n = fs.readJsonSync(__dirname + '/i18n/strings_en.json'); @@ -1163,11 +1163,11 @@ ControllerSpotify.prototype.loadI18n = function () { }; ControllerSpotify.prototype.getI18n = function (key) { - var self = this; + let self = this; if (key.indexOf('.') > 0) { - var mainKey = key.split('.')[0]; - var secKey = key.split('.')[1]; + let mainKey = key.split('.')[0]; + let secKey = key.split('.')[1]; if (self.i18n[mainKey][secKey] !== undefined) { return self.i18n[mainKey][secKey]; } else { @@ -1185,7 +1185,7 @@ ControllerSpotify.prototype.getI18n = function (key) { // BROWSING ControllerSpotify.prototype.addToBrowseSources = function () { - var data = { + let data = { name: 'Spotify', uri: 'spotify', plugin_type: 'music_service', @@ -1200,10 +1200,10 @@ ControllerSpotify.prototype.removeToBrowseSources = function () { }; ControllerSpotify.prototype.handleBrowseUri = function (curUri) { - var self = this; + let self = this; self.commandRouter.logger.info('In handleBrowseUri, curUri=' + curUri); - var response; + let response; if (curUri.startsWith('spotify')) { if (curUri == 'spotify') { @@ -1231,7 +1231,7 @@ ControllerSpotify.prototype.handleBrowseUri = function (curUri) { } else if (curUri.startsWith('spotify:user:')) { response = self.listWebPlaylist(curUri); } else if (curUri.startsWith('spotify:playlist:')) { - var uriSplitted = curUri.split(':'); + let uriSplitted = curUri.split(':'); response = self.listWebPlaylist('spotify:user:spotify:playlist:' + uriSplitted[2]); } else if (curUri.startsWith('spotify/new')) { response = self.listWebNew(curUri); @@ -1252,8 +1252,8 @@ ControllerSpotify.prototype.handleBrowseUri = function (curUri) { }; ControllerSpotify.prototype.getRoot = function () { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); self.browseCache.get('root', function (err, value) { if (!err) { @@ -1277,10 +1277,10 @@ ControllerSpotify.prototype.getRoot = function () { }; ControllerSpotify.prototype.listRoot = function (curUri) { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); - var response = { + let response = { navigation: { lists: [ { @@ -1357,7 +1357,7 @@ ControllerSpotify.prototype.listRoot = function (curUri) { }, }; - var spotifyRootArray = [ + let spotifyRootArray = [ self.featuredPlaylists('spotify/featuredplaylists'), self.listWebNew('spotify/new'), self.listWebCategories('spotify/categories'), @@ -1365,7 +1365,7 @@ ControllerSpotify.prototype.listRoot = function (curUri) { libQ .all(spotifyRootArray) .then(function (results) { - var discoveryArray = [ + let discoveryArray = [ { availableListViews: ['grid', 'list'], type: 'title', @@ -1397,11 +1397,11 @@ ControllerSpotify.prototype.listRoot = function (curUri) { }; ControllerSpotify.prototype.getMyPlaylists = function (curUri) { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); self.spotifyCheckAccessToken().then(function (data) { - var response = { + let response = { navigation: { prev: { uri: 'spotify', @@ -1416,8 +1416,8 @@ ControllerSpotify.prototype.getMyPlaylists = function (curUri) { }; self.spotifyApi.getUserPlaylists(self.loggedInUserId, { limit: 50 }).then( function (results) { - for (var i in results.body.items) { - var playlist = results.body.items[i]; + for (let i in results.body.items) { + let playlist = results.body.items[i]; response.navigation.lists[0].items.push({ service: 'spop', type: 'playlist', @@ -1450,8 +1450,8 @@ ControllerSpotify.prototype.getMyAlbums = function () { {}, { onData: (items) => { - for (var i in items) { - var album = items[i].album; + for (let i in items) { + let album = items[i].album; albums.push({ service: 'spop', type: 'folder', @@ -1503,8 +1503,8 @@ ControllerSpotify.prototype.getMyTracks = function () { {}, { onData: (items) => { - for (var i in items) { - var track = items[i].track; + for (let i in items) { + let track = items[i].track; if (this.isTrackAvailableInCountry(track)) { tracks.push({ service: 'spop', @@ -1566,7 +1566,7 @@ ControllerSpotify.prototype.getMyArtists = function () { { getItems: (data) => data.body?.artists?.items || [], onData: (items) => { - for (var i in items) { + for (let i in items) { const artist = items[i]; artists.push({ service: 'spop', @@ -1605,15 +1605,15 @@ ControllerSpotify.prototype.getMyArtists = function () { }; ControllerSpotify.prototype.getTopArtists = function (curUri) { - var self = this; + let self = this; - var defer = libQ.defer(); + let defer = libQ.defer(); self.spotifyCheckAccessToken().then(function (data) { - var spotifyDefer = self.spotifyApi.getMyTopArtists({ limit: 50 }); + let spotifyDefer = self.spotifyApi.getMyTopArtists({ limit: 50 }); spotifyDefer.then( function (results) { - var response = { + let response = { navigation: { prev: { uri: 'spotify', @@ -1627,8 +1627,8 @@ ControllerSpotify.prototype.getTopArtists = function (curUri) { }, }; - for (var i in results.body.items) { - var artist = results.body.items[i]; + for (let i in results.body.items) { + let artist = results.body.items[i]; response.navigation.lists[0].items.push({ service: 'spop', type: 'folder', @@ -1650,15 +1650,15 @@ ControllerSpotify.prototype.getTopArtists = function (curUri) { }; ControllerSpotify.prototype.getTopTracks = function (curUri) { - var self = this; + let self = this; - var defer = libQ.defer(); + let defer = libQ.defer(); self.spotifyCheckAccessToken().then(function (data) { - var spotifyDefer = self.spotifyApi.getMyTopTracks({ limit: 50 }); + let spotifyDefer = self.spotifyApi.getMyTopTracks({ limit: 50 }); spotifyDefer.then( function (results) { - var response = { + let response = { navigation: { prev: { uri: 'spotify', @@ -1672,8 +1672,8 @@ ControllerSpotify.prototype.getTopTracks = function (curUri) { }, }; - for (var i in results.body.items) { - var track = results.body.items[i]; + for (let i in results.body.items) { + let track = results.body.items[i]; if (self.isTrackAvailableInCountry(track)) { response.navigation.lists[0].items.push({ service: 'spop', @@ -1699,15 +1699,15 @@ ControllerSpotify.prototype.getTopTracks = function (curUri) { }; ControllerSpotify.prototype.getRecentTracks = function (curUri) { - var self = this; + let self = this; - var defer = libQ.defer(); + let defer = libQ.defer(); self.spotifyCheckAccessToken().then(function (data) { - var spotifyDefer = self.spotifyApi.getMyRecentlyPlayedTracks({ limit: 50 }); + let spotifyDefer = self.spotifyApi.getMyRecentlyPlayedTracks({ limit: 50 }); spotifyDefer.then( function (results) { - var response = { + let response = { navigation: { prev: { uri: 'spotify', @@ -1721,8 +1721,8 @@ ControllerSpotify.prototype.getRecentTracks = function (curUri) { }, }; - for (var i in results.body.items) { - var track = results.body.items[i].track; + for (let i in results.body.items) { + let track = results.body.items[i].track; if (self.isTrackAvailableInCountry(track)) { response.navigation.lists[0].items.push({ service: 'spop', @@ -1748,15 +1748,15 @@ ControllerSpotify.prototype.getRecentTracks = function (curUri) { }; ControllerSpotify.prototype.featuredPlaylists = function (curUri) { - var self = this; + let self = this; - var defer = libQ.defer(); + let defer = libQ.defer(); self.spotifyCheckAccessToken().then(function (data) { - var spotifyDefer = self.spotifyApi.getFeaturedPlaylists(); + let spotifyDefer = self.spotifyApi.getFeaturedPlaylists(); spotifyDefer.then( function (results) { - var response = { + let response = { navigation: { prev: { uri: 'spotify', @@ -1770,8 +1770,8 @@ ControllerSpotify.prototype.featuredPlaylists = function (curUri) { }, }; - for (var i in results.body.playlists.items) { - var playlist = results.body.playlists.items[i]; + for (let i in results.body.playlists.items) { + let playlist = results.body.playlists.items[i]; response.navigation.lists[0].items.push({ service: 'spop', type: 'playlist', @@ -1793,15 +1793,15 @@ ControllerSpotify.prototype.featuredPlaylists = function (curUri) { }; ControllerSpotify.prototype.listWebPlaylist = function (curUri) { - var self = this; + let self = this; - var defer = libQ.defer(); + let defer = libQ.defer(); - var uriSplitted = curUri.split(':'); + let uriSplitted = curUri.split(':'); - var spotifyDefer = self.getPlaylistTracks(uriSplitted[2], uriSplitted[4]); + let spotifyDefer = self.getPlaylistTracks(uriSplitted[2], uriSplitted[4]); spotifyDefer.then(function (results) { - var response = { + let response = { navigation: { prev: { uri: 'spotify', @@ -1814,10 +1814,10 @@ ControllerSpotify.prototype.listWebPlaylist = function (curUri) { ], }, }; - for (var i in results) { + for (let i in results) { response.navigation.lists[0].items.push(results[i]); } - var playlistInfo = self.getPlaylistInfo(uriSplitted[2], uriSplitted[4]); + let playlistInfo = self.getPlaylistInfo(uriSplitted[2], uriSplitted[4]); playlistInfo.then(function (results) { response.navigation.info = results; response.navigation.info.uri = curUri; @@ -1830,15 +1830,15 @@ ControllerSpotify.prototype.listWebPlaylist = function (curUri) { }; ControllerSpotify.prototype.listWebNew = function (curUri) { - var self = this; + let self = this; - var defer = libQ.defer(); + let defer = libQ.defer(); self.spotifyCheckAccessToken().then(function (data) { - var spotifyDefer = self.spotifyApi.getNewReleases({ limit: 50 }); + let spotifyDefer = self.spotifyApi.getNewReleases({ limit: 50 }); spotifyDefer.then( function (results) { - var response = { + let response = { navigation: { prev: { uri: 'spotify', @@ -1852,8 +1852,8 @@ ControllerSpotify.prototype.listWebNew = function (curUri) { }, }; - for (var i in results.body.albums.items) { - var album = results.body.albums.items[i]; + for (let i in results.body.albums.items) { + let album = results.body.albums.items[i]; response.navigation.lists[0].items.push({ service: 'spop', type: 'folder', @@ -1875,13 +1875,13 @@ ControllerSpotify.prototype.listWebNew = function (curUri) { }; ControllerSpotify.prototype.listWebAlbum = function (curUri) { - var self = this; - var defer = libQ.defer(); - var uriSplitted = curUri.split(':'); + let self = this; + let defer = libQ.defer(); + let uriSplitted = curUri.split(':'); - var spotifyDefer = self.getAlbumTracks(uriSplitted[2], { limit: 50 }); + let spotifyDefer = self.getAlbumTracks(uriSplitted[2], { limit: 50 }); spotifyDefer.then(function (results) { - var response = { + let response = { navigation: { prev: { uri: 'spotify', @@ -1895,10 +1895,10 @@ ControllerSpotify.prototype.listWebAlbum = function (curUri) { }, }; - for (var i in results) { + for (let i in results) { response.navigation.lists[0].items.push(results[i]); } - var albumInfo = self.getAlbumInfo(uriSplitted[2]); + let albumInfo = self.getAlbumInfo(uriSplitted[2]); albumInfo.then(function (results) { response.navigation.info = results; response.navigation.info.uri = curUri; @@ -1911,15 +1911,15 @@ ControllerSpotify.prototype.listWebAlbum = function (curUri) { }; ControllerSpotify.prototype.listWebCategories = function (curUri) { - var self = this; + let self = this; - var defer = libQ.defer(); + let defer = libQ.defer(); self.spotifyCheckAccessToken().then(function (data) { - var spotifyDefer = self.spotifyApi.getCategories({ limit: 50 }); + let spotifyDefer = self.spotifyApi.getCategories({ limit: 50 }); spotifyDefer.then( function (results) { - var response = { + let response = { navigation: { prev: { uri: 'spotify', @@ -1933,7 +1933,7 @@ ControllerSpotify.prototype.listWebCategories = function (curUri) { }, }; - for (var i in results.body.categories.items) { + for (let i in results.body.categories.items) { response.navigation.lists[0].items.push({ service: 'spop', type: 'spotify-category', @@ -1955,17 +1955,17 @@ ControllerSpotify.prototype.listWebCategories = function (curUri) { }; ControllerSpotify.prototype.listWebCategory = function (curUri) { - var self = this; + let self = this; - var defer = libQ.defer(); + let defer = libQ.defer(); - var uriSplitted = curUri.split('/'); + let uriSplitted = curUri.split('/'); self.spotifyCheckAccessToken().then(function (data) { - var spotifyDefer = self.spotifyApi.getPlaylistsForCategory(uriSplitted[2], { limit: 50 }); + let spotifyDefer = self.spotifyApi.getPlaylistsForCategory(uriSplitted[2], { limit: 50 }); spotifyDefer.then( function (results) { - var response = { + let response = { navigation: { prev: { uri: 'spotify/categories', @@ -1979,8 +1979,8 @@ ControllerSpotify.prototype.listWebCategory = function (curUri) { }, }; - for (var i in results.body.playlists.items) { - var playlist = results.body.playlists.items[i]; + for (let i in results.body.playlists.items) { + let playlist = results.body.playlists.items[i]; response.navigation.lists[0].items.push({ service: 'spop', type: 'folder', @@ -2002,16 +2002,16 @@ ControllerSpotify.prototype.listWebCategory = function (curUri) { }; ControllerSpotify.prototype.listWebArtist = function (curUri) { - var self = this; + let self = this; - var defer = libQ.defer(); + let defer = libQ.defer(); - var uriSplitted = curUri.split(':'); + let uriSplitted = curUri.split(':'); - var artistId = uriSplitted[2]; + let artistId = uriSplitted[2]; self.spotifyCheckAccessToken().then(function (data) { - var response = { + let response = { navigation: { prev: { uri: 'spotify', @@ -2035,10 +2035,10 @@ ControllerSpotify.prototype.listWebArtist = function (curUri) { ], }, }; - var spotifyDefer = self.listArtistTracks(artistId); + let spotifyDefer = self.listArtistTracks(artistId); spotifyDefer .then(function (results) { - for (var i in results) { + for (let i in results) { response.navigation.lists[0].items.push(results[i]); } return response; @@ -2047,7 +2047,7 @@ ControllerSpotify.prototype.listWebArtist = function (curUri) { return self.listArtistAlbums(artistId); }) .then(function (results) { - for (var i in results) { + for (let i in results) { response.navigation.lists[1].items.push(results[i]); } return response; @@ -2066,7 +2066,7 @@ ControllerSpotify.prototype.listWebArtist = function (curUri) { return self.getArtistRelatedArtists(artistId); }) .then(function (results) { - for (var i in results) { + for (let i in results) { response.navigation.lists[2].items.push(results[i]); } defer.resolve(response); @@ -2081,15 +2081,15 @@ ControllerSpotify.prototype.listWebArtist = function (curUri) { }; ControllerSpotify.prototype.listArtistTracks = function (id) { - var self = this; + let self = this; - var defer = libQ.defer(); + let defer = libQ.defer(); - var list = []; + let list = []; - var spotifyDefer = self.getArtistTopTracks(id); + let spotifyDefer = self.getArtistTopTracks(id); spotifyDefer.then(function (data) { - for (var i in data) { + for (let i in data) { list.push(data[i]); } defer.resolve(list); @@ -2099,15 +2099,15 @@ ControllerSpotify.prototype.listArtistTracks = function (id) { }; ControllerSpotify.prototype.listArtistAlbums = function (id) { - var self = this; + let self = this; - var defer = libQ.defer(); + let defer = libQ.defer(); - var spotifyDefer = self.spotifyApi.getArtistAlbums(id); + let spotifyDefer = self.spotifyApi.getArtistAlbums(id); spotifyDefer.then(function (results) { - var response = []; - for (var i in results.body.items) { - var album = results.body.items[i]; + let response = []; + for (let i in results.body.items) { + let album = results.body.items[i]; response.push({ service: 'spop', type: 'folder', @@ -2123,25 +2123,25 @@ ControllerSpotify.prototype.listArtistAlbums = function (id) { }; ControllerSpotify.prototype.getArtistTracks = function (id) { - var self = this; + let self = this; - var defer = libQ.defer(); + let defer = libQ.defer(); - var list = []; + let list = []; - var spotifyDefer = self.getArtistTopTracks(id); + let spotifyDefer = self.getArtistTopTracks(id); spotifyDefer .then(function (data) { - for (var i in data) { + for (let i in data) { list.push(data[i]); } return list; }) .then(function (data) { - var spotifyDefer = self.getArtistAlbumTracks(id); + let spotifyDefer = self.getArtistAlbumTracks(id); spotifyDefer.then(function (results) { - var response = data; - for (var i in results) { + let response = data; + for (let i in results) { response.push(results[i]); } defer.resolve(response); @@ -2152,30 +2152,30 @@ ControllerSpotify.prototype.getArtistTracks = function (id) { }; ControllerSpotify.prototype.getArtistAlbumTracks = function (id) { - var self = this; + let self = this; - var defer = libQ.defer(); + let defer = libQ.defer(); - var list = []; + let list = []; - var spotifyDefer = self.spotifyApi.getArtistAlbums(id); + let spotifyDefer = self.spotifyApi.getArtistAlbums(id); spotifyDefer .then(function (results) { // var response = data; - var response = []; + let response = []; return results.body.items.map(function (a) { return a.id; }); }) .then(function (albums) { - var spotifyDefer = self.spotifyApi.getAlbums(albums); + let spotifyDefer = self.spotifyApi.getAlbums(albums); spotifyDefer.then(function (data) { - var results = data; - var response = []; - for (var i in results.body.albums) { - var album = results.body.albums[i]; - for (var j in album.tracks.items) { - var track = album.tracks.items[j]; + let results = data; + let response = []; + for (let i in results.body.albums) { + let album = results.body.albums[i]; + for (let j in album.tracks.items) { + let track = album.tracks.items[j]; if (self.isTrackAvailableInCountry(track)) { response.push({ service: 'spop', @@ -2198,16 +2198,16 @@ ControllerSpotify.prototype.getArtistAlbumTracks = function (id) { }; ControllerSpotify.prototype.getArtistAlbums = function (artistId) { - var self = this; + let self = this; - var defer = libQ.defer(); + let defer = libQ.defer(); self.spotifyCheckAccessToken().then(function (data) { - var spotifyDefer = self.spotifyApi.getArtistAlbums(artistId); + let spotifyDefer = self.spotifyApi.getArtistAlbums(artistId); spotifyDefer.then(function (results) { - var response = []; - for (var i in results.body.items) { - var album = results.body.items[i]; + let response = []; + for (let i in results.body.items) { + let album = results.body.items[i]; response.push({ service: 'spop', type: 'folder', @@ -2223,20 +2223,20 @@ ControllerSpotify.prototype.getArtistAlbums = function (artistId) { }; ControllerSpotify.prototype.getArtistRelatedArtists = function (artistId) { - var self = this; + let self = this; - var defer = libQ.defer(); + let defer = libQ.defer(); - var list = []; + let list = []; self.spotifyCheckAccessToken().then(function (data) { - var spotifyDefer = self.spotifyApi.getArtistRelatedArtists(artistId); + let spotifyDefer = self.spotifyApi.getArtistRelatedArtists(artistId); spotifyDefer.then(function (results) { - for (var i in results.body.artists) { + for (let i in results.body.artists) { var albumart = ''; - var artist = results.body.artists[i]; + let artist = results.body.artists[i]; var albumart = self._getAlbumArt(artist); - var item = { + let item = { service: 'spop', type: 'folder', title: artist.name, @@ -2256,18 +2256,18 @@ ControllerSpotify.prototype.getArtistRelatedArtists = function (artistId) { }; ControllerSpotify.prototype.getAlbumTracks = function (id) { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); self.spotifyCheckAccessToken().then(function (data) { - var spotifyDefer = self.spotifyApi.getAlbum(id); + let spotifyDefer = self.spotifyApi.getAlbum(id); spotifyDefer.then( function (results) { - var response = []; - var album = results.body.name; - var albumart = results.body.images[0].url; - for (var i in results.body.tracks.items) { - var track = results.body.tracks.items[i]; + let response = []; + let album = results.body.name; + let albumart = results.body.images[0].url; + for (let i in results.body.tracks.items) { + let track = results.body.tracks.items[i]; if (self.isTrackAvailableInCountry(track)) { response.push({ service: 'spop', @@ -2300,8 +2300,8 @@ ControllerSpotify.prototype.getAlbumTracks = function (id) { }; ControllerSpotify.prototype.getPlaylistTracks = function (userId, playlistId) { - var defer = libQ.defer(); - var response = []; + let defer = libQ.defer(); + let response = []; this.spotifyCheckAccessToken().then(() => { fetchPagedData( @@ -2310,10 +2310,10 @@ ControllerSpotify.prototype.getPlaylistTracks = function (userId, playlistId) { { requiredArgs: [playlistId] }, { onData: (items) => { - for (var i in items) { - var track = items[i].track; + for (let i in items) { + let track = items[i].track; if (this.isTrackAvailableInCountry(track)) { - var item = { + let item = { service: 'spop', type: 'song', name: track.name, @@ -2350,16 +2350,16 @@ ControllerSpotify.prototype.getPlaylistTracks = function (userId, playlistId) { }; ControllerSpotify.prototype.getArtistTopTracks = function (id) { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); self.spotifyCheckAccessToken().then(function (data) { - var spotifyDefer = self.spotifyApi.getArtistTopTracks(id, 'GB'); + let spotifyDefer = self.spotifyApi.getArtistTopTracks(id, 'GB'); spotifyDefer.then(function (results) { - var response = []; - for (var i in results.body.tracks) { - var albumart = ''; - var track = results.body.tracks[i]; + let response = []; + for (let i in results.body.tracks) { + let albumart = ''; + let track = results.body.tracks[i]; if (track.album.hasOwnProperty('images') && track.album.images.length > 0) { albumart = track.album.images[0].url; } @@ -2394,12 +2394,12 @@ ControllerSpotify.prototype.getArtistTopTracks = function (id) { }; ControllerSpotify.prototype.getArtistInfo = function (id) { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); - var info = {}; + let info = {}; self.spotifyCheckAccessToken().then(function (data) { - var spotifyDefer = self.spotifyApi.getArtist(id); + let spotifyDefer = self.spotifyApi.getArtist(id); spotifyDefer.then(function (results) { if (results && results.body && results.body.name) { info.title = results.body.name; @@ -2418,12 +2418,12 @@ ControllerSpotify.prototype.getArtistInfo = function (id) { }; ControllerSpotify.prototype.getAlbumInfo = function (id) { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); - var info = {}; + let info = {}; self.spotifyCheckAccessToken().then(function (data) { - var spotifyDefer = self.spotifyApi.getAlbum(id); + let spotifyDefer = self.spotifyApi.getAlbum(id); spotifyDefer .then(function (results) { if (results && results.body && results.body.name) { @@ -2455,12 +2455,12 @@ ControllerSpotify.prototype.getAlbumInfo = function (id) { }; ControllerSpotify.prototype.getPlaylistInfo = function (userId, playlistId) { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); - var info = {}; + let info = {}; self.spotifyCheckAccessToken().then(function (data) { - var spotifyDefer = self.spotifyApi.getPlaylist(playlistId); + let spotifyDefer = self.spotifyApi.getPlaylist(playlistId); spotifyDefer.then( function (results) { if (results && results.body && results.body.name) { @@ -2482,16 +2482,16 @@ ControllerSpotify.prototype.getPlaylistInfo = function (userId, playlistId) { }; ControllerSpotify.prototype.getTrack = function (id) { - var defer = libQ.defer(); + let defer = libQ.defer(); this.spotifyCheckAccessToken().then(() => { rateLimitedCall(this.spotifyApi, 'getTrack', { args: [id], logger: this.logger }) .then((results) => { const track = results.body; - var response = []; - var artist = ''; - var album = ''; - var albumart = ''; + let response = []; + let artist = ''; + let album = ''; + let albumart = ''; if (track.artists.length > 0) { artist = track.artists[0].name; @@ -2507,7 +2507,7 @@ ControllerSpotify.prototype.getTrack = function (id) { albumart = ''; } - var item = { + let item = { uri: track.uri, service: 'spop', name: track.name, @@ -2535,18 +2535,18 @@ ControllerSpotify.prototype.getTrack = function (id) { // SEARCH FUNCTIONS ControllerSpotify.prototype.search = function (query) { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); self.spotifyCheckAccessToken().then(function (data) { - var spotifyDefer = self.spotifyApi.search(query.value, ['artist', 'album', 'playlist', 'track']); + let spotifyDefer = self.spotifyApi.search(query.value, ['artist', 'album', 'playlist', 'track']); spotifyDefer.then( function (results) { - var list = []; + let list = []; // Show artists, albums, playlists then tracks if (results.body.hasOwnProperty('artists') && results.body.artists.items.length > 0) { - var artistlist = []; - var artists = self._searchArtists(results); + let artistlist = []; + let artists = self._searchArtists(results); for (var i in artists) { artistlist.push(artists[i]); } @@ -2558,8 +2558,8 @@ ControllerSpotify.prototype.search = function (query) { }); } if (results.body.hasOwnProperty('albums') && results.body.albums.items.length > 0) { - var albumlist = []; - var albums = self._searchAlbums(results); + let albumlist = []; + let albums = self._searchAlbums(results); for (var i in albums) { albumlist.push(albums[i]); } @@ -2571,8 +2571,8 @@ ControllerSpotify.prototype.search = function (query) { }); } if (results.body.hasOwnProperty('playlists') && results.body.playlists.items.length > 0) { - var playlistlist = []; - var playlists = self._searchPlaylists(results); + let playlistlist = []; + let playlists = self._searchPlaylists(results); for (var i in playlists) { playlistlist.push(playlists[i]); } @@ -2584,8 +2584,8 @@ ControllerSpotify.prototype.search = function (query) { }); } if (results.body.hasOwnProperty('tracks') && results.body.tracks.items.length > 0) { - var songlist = []; - var tracks = self._searchTracks(results); + let songlist = []; + let tracks = self._searchTracks(results); for (var i in tracks) { songlist.push(tracks[i]); } @@ -2609,15 +2609,15 @@ ControllerSpotify.prototype.search = function (query) { }; ControllerSpotify.prototype._searchArtists = function (results) { - var list = []; + let list = []; - for (var i in results.body.artists.items) { - var albumart = ''; - var artist = results.body.artists.items[i]; + for (let i in results.body.artists.items) { + let albumart = ''; + let artist = results.body.artists.items[i]; if (artist.hasOwnProperty('images') && artist.images.length > 0) { albumart = artist.images[0].url; } - var item = { + let item = { service: 'spop', type: 'folder', title: artist.name, @@ -2634,15 +2634,15 @@ ControllerSpotify.prototype._searchArtists = function (results) { }; ControllerSpotify.prototype._searchAlbums = function (results) { - var list = []; + let list = []; - for (var i in results.body.albums.items) { - var albumart = ''; - var album = results.body.albums.items[i]; + for (let i in results.body.albums.items) { + let albumart = ''; + let album = results.body.albums.items[i]; if (album.hasOwnProperty('images') && album.images.length > 0) { albumart = album.images[0].url; } - var artist = ''; + let artist = ''; if (album.artists && album.artists[0] && album.artists[0].name) { artist = album.artists[0].name; } @@ -2661,11 +2661,11 @@ ControllerSpotify.prototype._searchAlbums = function (results) { }; ControllerSpotify.prototype._searchPlaylists = function (results) { - var list = []; + let list = []; - for (var i in results.body.playlists.items) { - var albumart = ''; - var playlist = results.body.playlists.items[i]; + for (let i in results.body.playlists.items) { + let albumart = ''; + let playlist = results.body.playlists.items[i]; if (playlist.hasOwnProperty('images') && playlist.images.length > 0) { albumart = playlist.images[0].url; } @@ -2682,11 +2682,11 @@ ControllerSpotify.prototype._searchPlaylists = function (results) { }; ControllerSpotify.prototype._searchTracks = function (results) { - var list = []; + let list = []; - for (var i in results.body.tracks.items) { - var albumart = ''; - var track = results.body.tracks.items[i]; + for (let i in results.body.tracks.items) { + let albumart = ''; + let track = results.body.tracks.items[i]; if (track.album.hasOwnProperty('images') && track.album.images.length > 0) { albumart = track.album.images[0].url; } @@ -2705,11 +2705,11 @@ ControllerSpotify.prototype._searchTracks = function (results) { }; ControllerSpotify.prototype._searchTracks = function (results) { - var list = []; + let list = []; - for (var i in results.body.tracks.items) { - var albumart = ''; - var track = results.body.tracks.items[i]; + for (let i in results.body.tracks.items) { + let albumart = ''; + let track = results.body.tracks.items[i]; if (track.album.hasOwnProperty('images') && track.album.images.length > 0) { albumart = track.album.images[0].url; } @@ -2728,12 +2728,12 @@ ControllerSpotify.prototype._searchTracks = function (results) { }; ControllerSpotify.prototype.searchArtistByName = function (artistName) { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); self.spotifyApi.search(artistName, ['artist']).then((results) => { if (results.body.hasOwnProperty('artists') && results.body.artists.items.length > 0) { - var artistResult = results.body.artists.items[0]; + let artistResult = results.body.artists.items[0]; self .listWebArtist('spotify:artist:' + artistResult.id) .then((result) => { @@ -2750,13 +2750,13 @@ ControllerSpotify.prototype.searchArtistByName = function (artistName) { }; ControllerSpotify.prototype.searchAlbumByName = function (albumName) { - var self = this; - var defer = libQ.defer(); + let self = this; + let defer = libQ.defer(); - var spotifyDefer = self.spotifyApi.search(albumName, ['album']); + let spotifyDefer = self.spotifyApi.search(albumName, ['album']); spotifyDefer.then((results) => { if (results.body.hasOwnProperty('albums') && results.body.albums.items.length > 0) { - var albumResult = results.body.albums.items[0]; + let albumResult = results.body.albums.items[0]; self .listWebAlbum('spotify:album:' + albumResult.id) .then((result) => { @@ -2773,7 +2773,7 @@ ControllerSpotify.prototype.searchAlbumByName = function (albumName) { }; ControllerSpotify.prototype.goto = function (data) { - var self = this; + let self = this; if (data.type == 'artist') { return self.searchArtistByName(data.value); @@ -2785,7 +2785,7 @@ ControllerSpotify.prototype.goto = function (data) { // PLUGIN FUNCTIONS ControllerSpotify.prototype.debugLog = function (stringToLog) { - var self = this; + let self = this; if (isDebugMode) { console.log('SPOTIFY: ' + stringToLog); @@ -2793,7 +2793,7 @@ ControllerSpotify.prototype.debugLog = function (stringToLog) { }; ControllerSpotify.prototype.isTrackAvailableInCountry = function (currentTrackObj) { - var self = this; + let self = this; if ( self.userCountry && @@ -2813,15 +2813,15 @@ ControllerSpotify.prototype.isTrackAvailableInCountry = function (currentTrackOb }; ControllerSpotify.prototype.explodeUri = function (uri) { - var self = this; + let self = this; self.debugLog('EXPLODING URI:' + uri); - var defer = libQ.defer(); + let defer = libQ.defer(); - var uriSplitted; + let uriSplitted; - var response; + let response; if (uri.startsWith('spotify/playlists')) { response = self.getMyPlaylists(); @@ -2854,7 +2854,7 @@ ControllerSpotify.prototype.explodeUri = function (uri) { }; ControllerSpotify.prototype.seekTimerAction = function () { - var self = this; + let self = this; if (this.state.status === 'play') { if (seekTimer === undefined) { @@ -2869,8 +2869,8 @@ ControllerSpotify.prototype.seekTimerAction = function () { }; ControllerSpotify.prototype.getLabelForSelect = function (options, key) { - var n = options.length; - for (var i = 0; i < n; i++) { + let n = options.length; + for (let i = 0; i < n; i++) { if (options[i].value === key) { return options[i].label; } @@ -2880,7 +2880,7 @@ ControllerSpotify.prototype.getLabelForSelect = function (options, key) { }; ControllerSpotify.prototype.getSpotifyVolume = function () { - var self = this; + let self = this; self.logger.info('Getting Spotify volume'); superagent diff --git a/spotify/wsclient.js b/spotify/wsclient.js index 32abf2085..bc335a918 100644 --- a/spotify/wsclient.js +++ b/spotify/wsclient.js @@ -1,7 +1,7 @@ //import WebSocket from 'ws'; -var websocket = require('ws'); +let websocket = require('ws'); -var ws = new websocket('ws://localhost:9876/events'); +let ws = new websocket('ws://localhost:9876/events'); ws.on('error', function (error) { console.error(error); From f9e77e80d98ed389a02bc8979fce4ea97c5de04a Mon Sep 17 00:00:00 2001 From: Phil Tsaryk Date: Mon, 29 Jan 2024 22:56:22 +0100 Subject: [PATCH 4/6] fix: fix crucial errors found by eslint --- spotify/index.js | 108 +++++++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 51 deletions(-) diff --git a/spotify/index.js b/spotify/index.js index 4ea7d5d68..13303c375 100644 --- a/spotify/index.js +++ b/spotify/index.js @@ -285,6 +285,7 @@ ControllerSpotify.prototype.parseEventState = function (event) { //self.state.status = 'play'; pushStateforEvent = false; self.alignSpotifyVolumeToVolumioVolume(); + break; case 'volume': try { if (event.data && event.data.value !== undefined) { @@ -893,7 +894,6 @@ ControllerSpotify.prototype.oauthLogin = function (data) { config.then(function (conf) { self.commandRouter.broadcastMessage('pushUiConfig', conf); self.commandRouter.broadcastMessage('closeAllModals', ''); - defer.resolve(conf); }); }) .fail(function (e) { @@ -979,7 +979,7 @@ ControllerSpotify.prototype.deleteCredentialsFile = function () { try { fs.unlinkSync(credentialsPath); } catch (err) { - self.logger.error('Failed to delete credentials file ' + e); + self.logger.error('Failed to delete credentials file ' + err); } }; @@ -2355,39 +2355,41 @@ ControllerSpotify.prototype.getArtistTopTracks = function (id) { self.spotifyCheckAccessToken().then(function (data) { let spotifyDefer = self.spotifyApi.getArtistTopTracks(id, 'GB'); - spotifyDefer.then(function (results) { - let response = []; - for (let i in results.body.tracks) { - let albumart = ''; - let track = results.body.tracks[i]; - if (track.album.hasOwnProperty('images') && track.album.images.length > 0) { - albumart = track.album.images[0].url; - } - if (self.isTrackAvailableInCountry(track)) { - response.push({ - service: 'spop', - type: 'song', - name: track.name, - title: track.name, - artist: track.artists[0].name, - album: track.album.name, - albumart: albumart, - duration: parseInt(track.duration_ms / 1000), - samplerate: self.getCurrentBitrate(), - bitdepth: '16 bit', - bitrate: '', - codec: 'ogg', - trackType: 'spotify', - uri: track.uri, - }); + spotifyDefer.then( + function (results) { + let response = []; + for (let i in results.body.tracks) { + let albumart = ''; + let track = results.body.tracks[i]; + if (track.album.hasOwnProperty('images') && track.album.images.length > 0) { + albumart = track.album.images[0].url; + } + if (self.isTrackAvailableInCountry(track)) { + response.push({ + service: 'spop', + type: 'song', + name: track.name, + title: track.name, + artist: track.artists[0].name, + album: track.album.name, + albumart: albumart, + duration: parseInt(track.duration_ms / 1000), + samplerate: self.getCurrentBitrate(), + bitdepth: '16 bit', + bitrate: '', + codec: 'ogg', + trackType: 'spotify', + uri: track.uri, + }); + } } - } - defer.resolve(response); - }), + defer.resolve(response); + }, function (err) { self.logger.error('An error occurred while listing Spotify artist tracks ' + err); defer.reject(''); - }; + }, + ); }); return defer.promise; @@ -2400,18 +2402,20 @@ ControllerSpotify.prototype.getArtistInfo = function (id) { let info = {}; self.spotifyCheckAccessToken().then(function (data) { let spotifyDefer = self.spotifyApi.getArtist(id); - spotifyDefer.then(function (results) { - if (results && results.body && results.body.name) { - info.title = results.body.name; - info.albumart = results.body.images[0].url; - info.type = 'artist'; - } - defer.resolve(info); - }), + spotifyDefer.then( + function (results) { + if (results && results.body && results.body.name) { + info.title = results.body.name; + info.albumart = results.body.images[0].url; + info.type = 'artist'; + } + defer.resolve(info); + }, function (err) { self.logger.info('An error occurred while listing Spotify artist informations ' + err); defer.resolve(info); - }; + }, + ); }); return defer.promise; @@ -2438,17 +2442,19 @@ ControllerSpotify.prototype.getAlbumInfo = function (id) { .then(function (artist) { return self.spotifyApi.getArtist(artist); }) - .then(function (artistResults) { - if (artistResults && artistResults.body && artistResults.body.name) { - info.artistImage = artistResults.body.images[0].url; - info.artistUri = artistResults.body.uri; - } - defer.resolve(info); - }), - function (err) { - self.logger.error('An error occurred while listing Spotify album informations ' + err); - defer.resolve(info); - }; + .then( + function (artistResults) { + if (artistResults && artistResults.body && artistResults.body.name) { + info.artistImage = artistResults.body.images[0].url; + info.artistUri = artistResults.body.uri; + } + defer.resolve(info); + }, + function (err) { + self.logger.error('An error occurred while listing Spotify album informations ' + err); + defer.resolve(info); + }, + ); }); return defer.promise; From f3f1e2da00753fef9564d364599c6c17aeb793a8 Mon Sep 17 00:00:00 2001 From: Phil Tsaryk Date: Mon, 29 Jan 2024 23:01:26 +0100 Subject: [PATCH 5/6] fix: fix some eslint warnings --- spotify/index.js | 110 ++++++++++++++++++----------------------------- 1 file changed, 43 insertions(+), 67 deletions(-) diff --git a/spotify/index.js b/spotify/index.js index 13303c375..f118566db 100644 --- a/spotify/index.js +++ b/spotify/index.js @@ -4,7 +4,7 @@ let libQ = require('kew'); let fs = require('fs-extra'); let config = new (require('v-conf'))(); let superagent = require('superagent'); -var os = require('os'); +let os = require('os'); let websocket = require('ws'); let path = require('path'); let SpotifyWebApi = require('spotify-web-api-node'); @@ -12,7 +12,6 @@ let io = require('socket.io-client'); let exec = require('child_process').exec; let execSync = require('child_process').execSync; let NodeCache = require('node-cache'); -var os = require('os'); let { fetchPagedData, rateLimitedCall } = require('./utils/extendedSpotifyApi'); let configFileDestinationPath = '/tmp/go-librespot-config.yml'; @@ -49,8 +48,6 @@ module.exports = ControllerSpotify; function ControllerSpotify(context) { // This fixed variable will let us refer to 'this' object at deeper scopes - let self = this; - this.context = context; this.commandRouter = this.context.coreCommand; this.logger = this.context.logger; @@ -59,7 +56,6 @@ function ControllerSpotify(context) { } ControllerSpotify.prototype.onVolumioStart = function () { - let self = this; let configFile = this.commandRouter.pluginManager.getConfigurationFile(this.context, 'config.json'); this.config = new (require('v-conf'))(); this.config.loadFile(configFile); @@ -213,8 +209,6 @@ ControllerSpotify.prototype.initializeSpotifyControls = function () { }; ControllerSpotify.prototype.resetSpotifyState = function () { - let self = this; - this.state = { status: 'stop', service: 'spop', @@ -363,8 +357,6 @@ ControllerSpotify.prototype.initializeSpotifyPlaybackInVolatileMode = function ( }; ControllerSpotify.prototype.parseDuration = function (spotifyDuration) { - let self = this; - try { return parseInt(spotifyDuration / 1000); } catch (e) { @@ -379,8 +371,6 @@ ControllerSpotify.prototype.getCurrentBitrate = function () { }; ControllerSpotify.prototype.parseArtists = function (spotifyArtists) { - let self = this; - let artist = ''; if (spotifyArtists.length > 0) { for (let i in spotifyArtists) { @@ -423,7 +413,7 @@ ControllerSpotify.prototype.getState = function () { }; // Announce updated Spop state -ControllerSpotify.prototype.pushState = function (state) { +ControllerSpotify.prototype.pushState = function () { let self = this; self.state.bitrate = self.getCurrentBitrate(); @@ -439,7 +429,7 @@ ControllerSpotify.prototype.sendSpotifyLocalApiCommand = function (commandPath) superagent .post(spotifyLocalApiEndpointBase + commandPath) .accept('application/json') - .then((results) => {}) + .then(() => {}) .catch((error) => { this.logger.error('Failed to send command to Spotify local API: ' + commandPath + ': ' + error); }); @@ -452,7 +442,7 @@ ControllerSpotify.prototype.sendSpotifyLocalApiCommandWithPayload = function (co .post(spotifyLocalApiEndpointBase + commandPath) .accept('application/json') .send(payload) - .then((results) => {}) + .then(() => {}) .catch((error) => { this.logger.error('Failed to send command to Spotify local API: ' + commandPath + ': ' + error); }); @@ -682,7 +672,7 @@ ControllerSpotify.prototype.startLibrespotDaemon = function () { let self = this; let defer = libQ.defer(); - exec('/usr/bin/sudo systemctl restart go-librespot-daemon.service', function (error, stdout, stderr) { + exec('/usr/bin/sudo systemctl restart go-librespot-daemon.service', function (error) { if (error) { self.logger.error('Cannot start Go-librespot Daemon: ' + error); defer.reject(error); @@ -700,7 +690,7 @@ ControllerSpotify.prototype.stopLibrespotDaemon = function () { let self = this; let defer = libQ.defer(); - exec('/usr/bin/sudo systemctl stop go-librespot-daemon.service', function (error, stdout, stderr) { + exec('/usr/bin/sudo systemctl stop go-librespot-daemon.service', function (error) { if (error) { self.logger.error('Cannot stop Go-librespot Daemon: ' + error); defer.reject(error); @@ -786,15 +776,10 @@ ControllerSpotify.prototype.isOauthLoginAlreadyConfiguredOnDaemon = function () } }; -ControllerSpotify.prototype.saveGoLibrespotSettings = function (data, avoidBroadcastUiConfig) { +ControllerSpotify.prototype.saveGoLibrespotSettings = function (data) { let self = this; let defer = libQ.defer(); - let broadcastUiConfig = true; - if (avoidBroadcastUiConfig === true) { - broadcastUiConfig = false; - } - if (data.bitrate !== undefined && data.bitrate.value !== undefined) { self.config.set('bitrate_number', data.bitrate.value); } @@ -986,17 +971,16 @@ ControllerSpotify.prototype.deleteCredentialsFile = function () { ControllerSpotify.prototype.spotifyApiConnect = function () { let self = this; let defer = libQ.defer(); - let d = new Date(); self.spotifyApi = new SpotifyWebApi(); // Retrieve an access token self.spotifyClientCredentialsGrant().then( - function (data) { + function () { self.logger.info('Spotify credentials grant success - running version from March 24, 2019'); self .getUserInformations() - .then(function (data) { + .then(function () { defer.resolve(); }) .fail(function (err) { @@ -1276,7 +1260,7 @@ ControllerSpotify.prototype.getRoot = function () { return defer.promise; }; -ControllerSpotify.prototype.listRoot = function (curUri) { +ControllerSpotify.prototype.listRoot = function () { let self = this; let defer = libQ.defer(); @@ -1396,11 +1380,11 @@ ControllerSpotify.prototype.listRoot = function (curUri) { return defer.promise; }; -ControllerSpotify.prototype.getMyPlaylists = function (curUri) { +ControllerSpotify.prototype.getMyPlaylists = function () { let self = this; let defer = libQ.defer(); - self.spotifyCheckAccessToken().then(function (data) { + self.spotifyCheckAccessToken().then(function () { let response = { navigation: { prev: { @@ -1604,12 +1588,12 @@ ControllerSpotify.prototype.getMyArtists = function () { return defer.promise; }; -ControllerSpotify.prototype.getTopArtists = function (curUri) { +ControllerSpotify.prototype.getTopArtists = function () { let self = this; let defer = libQ.defer(); - self.spotifyCheckAccessToken().then(function (data) { + self.spotifyCheckAccessToken().then(function () { let spotifyDefer = self.spotifyApi.getMyTopArtists({ limit: 50 }); spotifyDefer.then( function (results) { @@ -1649,12 +1633,12 @@ ControllerSpotify.prototype.getTopArtists = function (curUri) { return defer.promise; }; -ControllerSpotify.prototype.getTopTracks = function (curUri) { +ControllerSpotify.prototype.getTopTracks = function () { let self = this; let defer = libQ.defer(); - self.spotifyCheckAccessToken().then(function (data) { + self.spotifyCheckAccessToken().then(function () { let spotifyDefer = self.spotifyApi.getMyTopTracks({ limit: 50 }); spotifyDefer.then( function (results) { @@ -1698,12 +1682,12 @@ ControllerSpotify.prototype.getTopTracks = function (curUri) { return defer.promise; }; -ControllerSpotify.prototype.getRecentTracks = function (curUri) { +ControllerSpotify.prototype.getRecentTracks = function () { let self = this; let defer = libQ.defer(); - self.spotifyCheckAccessToken().then(function (data) { + self.spotifyCheckAccessToken().then(function () { let spotifyDefer = self.spotifyApi.getMyRecentlyPlayedTracks({ limit: 50 }); spotifyDefer.then( function (results) { @@ -1747,12 +1731,12 @@ ControllerSpotify.prototype.getRecentTracks = function (curUri) { return defer.promise; }; -ControllerSpotify.prototype.featuredPlaylists = function (curUri) { +ControllerSpotify.prototype.featuredPlaylists = function () { let self = this; let defer = libQ.defer(); - self.spotifyCheckAccessToken().then(function (data) { + self.spotifyCheckAccessToken().then(function () { let spotifyDefer = self.spotifyApi.getFeaturedPlaylists(); spotifyDefer.then( function (results) { @@ -1829,12 +1813,12 @@ ControllerSpotify.prototype.listWebPlaylist = function (curUri) { return defer.promise; }; -ControllerSpotify.prototype.listWebNew = function (curUri) { +ControllerSpotify.prototype.listWebNew = function () { let self = this; let defer = libQ.defer(); - self.spotifyCheckAccessToken().then(function (data) { + self.spotifyCheckAccessToken().then(function () { let spotifyDefer = self.spotifyApi.getNewReleases({ limit: 50 }); spotifyDefer.then( function (results) { @@ -1910,12 +1894,12 @@ ControllerSpotify.prototype.listWebAlbum = function (curUri) { return defer.promise; }; -ControllerSpotify.prototype.listWebCategories = function (curUri) { +ControllerSpotify.prototype.listWebCategories = function () { let self = this; let defer = libQ.defer(); - self.spotifyCheckAccessToken().then(function (data) { + self.spotifyCheckAccessToken().then(function () { let spotifyDefer = self.spotifyApi.getCategories({ limit: 50 }); spotifyDefer.then( function (results) { @@ -1961,7 +1945,7 @@ ControllerSpotify.prototype.listWebCategory = function (curUri) { let uriSplitted = curUri.split('/'); - self.spotifyCheckAccessToken().then(function (data) { + self.spotifyCheckAccessToken().then(function () { let spotifyDefer = self.spotifyApi.getPlaylistsForCategory(uriSplitted[2], { limit: 50 }); spotifyDefer.then( function (results) { @@ -2010,7 +1994,7 @@ ControllerSpotify.prototype.listWebArtist = function (curUri) { let artistId = uriSplitted[2]; - self.spotifyCheckAccessToken().then(function (data) { + self.spotifyCheckAccessToken().then(function () { let response = { navigation: { prev: { @@ -2043,7 +2027,7 @@ ControllerSpotify.prototype.listWebArtist = function (curUri) { } return response; }) - .then(function (results) { + .then(function () { return self.listArtistAlbums(artistId); }) .then(function (results) { @@ -2052,7 +2036,7 @@ ControllerSpotify.prototype.listWebArtist = function (curUri) { } return response; }) - .then(function (results) { + .then(function () { return self.getArtistInfo(artistId); }) .then(function (results) { @@ -2062,7 +2046,7 @@ ControllerSpotify.prototype.listWebArtist = function (curUri) { return response; }) - .then(function (results) { + .then(function () { return self.getArtistRelatedArtists(artistId); }) .then(function (results) { @@ -2072,7 +2056,7 @@ ControllerSpotify.prototype.listWebArtist = function (curUri) { defer.resolve(response); return response; }) - .catch(function (error) { + .catch(function () { defer.resolve(response); }); }); @@ -2156,13 +2140,10 @@ ControllerSpotify.prototype.getArtistAlbumTracks = function (id) { let defer = libQ.defer(); - let list = []; - let spotifyDefer = self.spotifyApi.getArtistAlbums(id); spotifyDefer .then(function (results) { // var response = data; - let response = []; return results.body.items.map(function (a) { return a.id; }); @@ -2202,7 +2183,7 @@ ControllerSpotify.prototype.getArtistAlbums = function (artistId) { let defer = libQ.defer(); - self.spotifyCheckAccessToken().then(function (data) { + self.spotifyCheckAccessToken().then(function () { let spotifyDefer = self.spotifyApi.getArtistAlbums(artistId); spotifyDefer.then(function (results) { let response = []; @@ -2229,13 +2210,12 @@ ControllerSpotify.prototype.getArtistRelatedArtists = function (artistId) { let list = []; - self.spotifyCheckAccessToken().then(function (data) { + self.spotifyCheckAccessToken().then(function () { let spotifyDefer = self.spotifyApi.getArtistRelatedArtists(artistId); spotifyDefer.then(function (results) { for (let i in results.body.artists) { - var albumart = ''; let artist = results.body.artists[i]; - var albumart = self._getAlbumArt(artist); + let albumart = self._getAlbumArt(artist); let item = { service: 'spop', type: 'folder', @@ -2259,7 +2239,7 @@ ControllerSpotify.prototype.getAlbumTracks = function (id) { let self = this; let defer = libQ.defer(); - self.spotifyCheckAccessToken().then(function (data) { + self.spotifyCheckAccessToken().then(function () { let spotifyDefer = self.spotifyApi.getAlbum(id); spotifyDefer.then( function (results) { @@ -2353,7 +2333,7 @@ ControllerSpotify.prototype.getArtistTopTracks = function (id) { let self = this; let defer = libQ.defer(); - self.spotifyCheckAccessToken().then(function (data) { + self.spotifyCheckAccessToken().then(function () { let spotifyDefer = self.spotifyApi.getArtistTopTracks(id, 'GB'); spotifyDefer.then( function (results) { @@ -2400,7 +2380,7 @@ ControllerSpotify.prototype.getArtistInfo = function (id) { let defer = libQ.defer(); let info = {}; - self.spotifyCheckAccessToken().then(function (data) { + self.spotifyCheckAccessToken().then(function () { let spotifyDefer = self.spotifyApi.getArtist(id); spotifyDefer.then( function (results) { @@ -2426,7 +2406,7 @@ ControllerSpotify.prototype.getAlbumInfo = function (id) { let defer = libQ.defer(); let info = {}; - self.spotifyCheckAccessToken().then(function (data) { + self.spotifyCheckAccessToken().then(function () { let spotifyDefer = self.spotifyApi.getAlbum(id); spotifyDefer .then(function (results) { @@ -2465,7 +2445,7 @@ ControllerSpotify.prototype.getPlaylistInfo = function (userId, playlistId) { let defer = libQ.defer(); let info = {}; - self.spotifyCheckAccessToken().then(function (data) { + self.spotifyCheckAccessToken().then(function () { let spotifyDefer = self.spotifyApi.getPlaylist(playlistId); spotifyDefer.then( function (results) { @@ -2544,7 +2524,7 @@ ControllerSpotify.prototype.search = function (query) { let self = this; let defer = libQ.defer(); - self.spotifyCheckAccessToken().then(function (data) { + self.spotifyCheckAccessToken().then(function () { let spotifyDefer = self.spotifyApi.search(query.value, ['artist', 'album', 'playlist', 'track']); spotifyDefer.then( function (results) { @@ -2553,7 +2533,7 @@ ControllerSpotify.prototype.search = function (query) { if (results.body.hasOwnProperty('artists') && results.body.artists.items.length > 0) { let artistlist = []; let artists = self._searchArtists(results); - for (var i in artists) { + for (const i in artists) { artistlist.push(artists[i]); } list.push({ @@ -2566,7 +2546,7 @@ ControllerSpotify.prototype.search = function (query) { if (results.body.hasOwnProperty('albums') && results.body.albums.items.length > 0) { let albumlist = []; let albums = self._searchAlbums(results); - for (var i in albums) { + for (const i in albums) { albumlist.push(albums[i]); } list.push({ @@ -2579,7 +2559,7 @@ ControllerSpotify.prototype.search = function (query) { if (results.body.hasOwnProperty('playlists') && results.body.playlists.items.length > 0) { let playlistlist = []; let playlists = self._searchPlaylists(results); - for (var i in playlists) { + for (const i in playlists) { playlistlist.push(playlists[i]); } list.push({ @@ -2592,7 +2572,7 @@ ControllerSpotify.prototype.search = function (query) { if (results.body.hasOwnProperty('tracks') && results.body.tracks.items.length > 0) { let songlist = []; let tracks = self._searchTracks(results); - for (var i in tracks) { + for (const i in tracks) { songlist.push(tracks[i]); } list.push({ @@ -2791,8 +2771,6 @@ ControllerSpotify.prototype.goto = function (data) { // PLUGIN FUNCTIONS ControllerSpotify.prototype.debugLog = function (stringToLog) { - let self = this; - if (isDebugMode) { console.log('SPOTIFY: ' + stringToLog); } @@ -2860,8 +2838,6 @@ ControllerSpotify.prototype.explodeUri = function (uri) { }; ControllerSpotify.prototype.seekTimerAction = function () { - let self = this; - if (this.state.status === 'play') { if (seekTimer === undefined) { seekTimer = setInterval(() => { From 3ca9f74a2ab484e963e2613ef302e41454f1fa54 Mon Sep 17 00:00:00 2001 From: Phil Tsaryk Date: Fri, 9 Feb 2024 09:12:30 +0100 Subject: [PATCH 6/6] ci: integrate GitHub Actions --- .github/workflows/lint.yml | 28 ++++++++++++++++++++++++++++ .nvmrc | 1 + .prettierignore | 1 + package.json | 6 ++++-- 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/lint.yml create mode 100644 .nvmrc diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..0c5eb8061 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,28 @@ +name: Lint + +on: + pull_request_target: + branches: + - master + +env: + NODE_VERSION: 16 + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Install NodeJS + uses: actions/setup-node@v2 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Checkout + uses: actions/checkout@v2 + + - name: Install dependencies + run: npm ci + + - name: Lint + run: npm run lint diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..b6a7d89c6 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +16 diff --git a/.prettierignore b/.prettierignore index b48468f03..e62b83d48 100644 --- a/.prettierignore +++ b/.prettierignore @@ -10,4 +10,5 @@ # add more exceptions to enable for other plugins # ... +# those should be the last entries to ignore them again after exceptions node_modules/ diff --git a/package.json b/package.json index 205838df4..0fdd1f359 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,10 @@ "version": "0.0.0", "private": true, "scripts": { - "format": "prettier --write '**/*.{json,md}' && npm run lint -- --fix", - "lint": "eslint .", + "format": "prettier --write '**/*.{json,md}' && npm run lint:es -- --fix", + "lint": "npm run lint:es && npm run lint:prettier", + "lint:es": "eslint .", + "lint:prettier": "prettier --check '**/*.{json,md}'", "prepare": "husky install" }, "devDependencies": {