Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
44fc2bb621
|
|||
7cdfa00af5
|
|||
35572a48e7
|
|||
3186f375e1
|
|||
3c1b7dba0e
|
|||
5591070979
|
|||
38e8b4c2ba
|
|||
260cef7b73
|
|||
c57f9ec3ea
|
|||
6874804703
|
|||
8cb5ab8340
|
|||
270cd9ad05
|
|||
dfd6d559bf | |||
7f616b4c7d | |||
b2e6d20d27 |
@ -8,6 +8,7 @@ MASTODON_INSTANCE = 'metalhead.club'
|
|||||||
MASTODON_ACCESS_TOKEN = 'YOUR_ACCESS_TOKEN_HERE'
|
MASTODON_ACCESS_TOKEN = 'YOUR_ACCESS_TOKEN_HERE'
|
||||||
BASE_URL = 'https://moshingmammut.phlaym.net'
|
BASE_URL = 'https://moshingmammut.phlaym.net'
|
||||||
VERBOSE = false
|
VERBOSE = false
|
||||||
|
DEBUG_LOG = false
|
||||||
IGNORE_USERS = @moshhead@metalhead.club
|
IGNORE_USERS = @moshhead@metalhead.club
|
||||||
WEBSUB_HUB = 'http://pubsubhubbub.superfeedr.com'
|
WEBSUB_HUB = 'http://pubsubhubbub.superfeedr.com'
|
||||||
|
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,7 @@
|
|||||||
|
moshing-mammut.pid
|
||||||
yt_auth_token
|
yt_auth_token
|
||||||
spotify_auth_token
|
spotify_auth_token
|
||||||
|
tidal_auth_token
|
||||||
*.db
|
*.db
|
||||||
feed.xml
|
feed.xml
|
||||||
playbook.yml
|
playbook.yml
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"apexskier.eslint.config.eslintConfigPath" : ".eslintrc.cjs",
|
"apexskier.eslint.config.eslintConfigPath" : ".eslint.cjs",
|
||||||
"apexskier.eslint.config.eslintPath" : "node_modules\/@eslint\/eslintrc\/dist\/eslintrc.cjs",
|
|
||||||
"apexskier.eslint.config.fixOnSave" : "Enable",
|
"apexskier.eslint.config.fixOnSave" : "Enable",
|
||||||
"apexskier.typescript.config.formatDocumentOnSave" : "false",
|
"apexskier.typescript.config.formatDocumentOnSave" : "false",
|
||||||
"apexskier.typescript.config.isEnabledForJavascript" : "Enable",
|
"apexskier.typescript.config.isEnabledForJavascript" : "Enable",
|
||||||
|
@ -5,6 +5,5 @@
|
|||||||
"trailingComma": "none",
|
"trailingComma": "none",
|
||||||
"printWidth": 100,
|
"printWidth": 100,
|
||||||
"plugins": ["prettier-plugin-svelte"],
|
"plugins": ["prettier-plugin-svelte"],
|
||||||
"pluginSearchDirs": ["."],
|
|
||||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||||
}
|
}
|
||||||
|
@ -151,3 +151,5 @@ Other icons:
|
|||||||
- [error-warning-fill by remix icon](https://remixicon.com/icon/error-warning-fill)
|
- [error-warning-fill by remix icon](https://remixicon.com/icon/error-warning-fill)
|
||||||
- [git-branch-fill by remix icon](https://remixicon.com/icon/git-branch-fill)
|
- [git-branch-fill by remix icon](https://remixicon.com/icon/git-branch-fill)
|
||||||
- [rss-fill by remix icon](https://remixicon.com/icon/rss-line)
|
- [rss-fill by remix icon](https://remixicon.com/icon/rss-line)
|
||||||
|
- [spotify-fill by remix icon](https://remixicon.com/icon/spotify-fill)
|
||||||
|
- [youtube-fill by remix icon](https://remixicon.com/icon/youtube-fill)
|
||||||
|
73
eslint.config.cjs
Normal file
73
eslint.config.cjs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
const { defineConfig, globalIgnores } = require('eslint/config');
|
||||||
|
|
||||||
|
const tsParser = require('@typescript-eslint/parser');
|
||||||
|
const typescriptEslint = require('@typescript-eslint/eslint-plugin');
|
||||||
|
const parser = require('svelte-eslint-parser');
|
||||||
|
const globals = require('globals');
|
||||||
|
const js = require('@eslint/js');
|
||||||
|
|
||||||
|
const { FlatCompat } = require('@eslint/eslintrc');
|
||||||
|
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: __dirname,
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
allConfig: js.configs.all
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = defineConfig([
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
parser: tsParser,
|
||||||
|
sourceType: 'module',
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
|
||||||
|
parserOptions: {
|
||||||
|
extraFileExtensions: ['.svelte']
|
||||||
|
},
|
||||||
|
|
||||||
|
globals: {
|
||||||
|
...globals.browser,
|
||||||
|
...globals.node
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
extends: compat.extends(
|
||||||
|
'plugin:svelte/recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'prettier'
|
||||||
|
),
|
||||||
|
|
||||||
|
plugins: {
|
||||||
|
'@typescript-eslint': typescriptEslint
|
||||||
|
},
|
||||||
|
|
||||||
|
settings: {
|
||||||
|
'svelte3/typescript': () => require('typescript')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
globalIgnores(['**/*.cjs']),
|
||||||
|
{
|
||||||
|
files: ['**/*.svelte'],
|
||||||
|
|
||||||
|
languageOptions: {
|
||||||
|
parser: parser,
|
||||||
|
|
||||||
|
parserOptions: {
|
||||||
|
parser: '@typescript-eslint/parser'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
globalIgnores([
|
||||||
|
'**/.DS_Store',
|
||||||
|
'**/node_modules',
|
||||||
|
'build',
|
||||||
|
'.svelte-kit',
|
||||||
|
'package',
|
||||||
|
'**/.env',
|
||||||
|
'**/.env.*',
|
||||||
|
'!**/.env.example',
|
||||||
|
'**/pnpm-lock.yaml',
|
||||||
|
'**/package-lock.json',
|
||||||
|
'**/yarn.lock'
|
||||||
|
])
|
||||||
|
]);
|
@ -1,14 +1,17 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Moshing Mammut
|
Description=Moshing Mammut
|
||||||
|
After=network.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/home/moshing-mammut/app/start.sh
|
ExecStart=/home/moshing-mammut/app/start.sh
|
||||||
Restart=always
|
Restart=on-failure
|
||||||
User=moshing-mammut
|
User=moshing-mammut
|
||||||
Group=moshing-mammut
|
Group=moshing-mammut
|
||||||
Environment=PATH=/usr/bin:/usr/local/bin
|
Environment=PATH=/usr/bin:/usr/local/bin
|
||||||
Environment=NODE_ENV=production
|
Environment=NODE_ENV=production
|
||||||
WorkingDirectory=/home/moshing-mammut/app
|
WorkingDirectory=/home/moshing-mammut/app
|
||||||
|
Type=forking
|
||||||
|
PIDFile=/home/moshing-mammut/app/moshing-mammut.pid
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
477
package-lock.json
generated
477
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "moshing-mammut",
|
"name": "moshing-mammut",
|
||||||
"version": "1.4.0",
|
"version": "2.0.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "moshing-mammut",
|
"name": "moshing-mammut",
|
||||||
"version": "1.4.0",
|
"version": "2.0.1",
|
||||||
"license": "LGPL-3.0-or-later",
|
"license": "LGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dotenv": "^17.0.0",
|
"dotenv": "^17.0.0",
|
||||||
@ -16,6 +16,8 @@
|
|||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
|
"@eslint/js": "^9.30.1",
|
||||||
"@sveltejs/adapter-node": "^5.2.12",
|
"@sveltejs/adapter-node": "^5.2.12",
|
||||||
"@sveltejs/kit": "^2.22.2",
|
"@sveltejs/kit": "^2.22.2",
|
||||||
"@sveltejs/vite-plugin-svelte": "^5.1.0",
|
"@sveltejs/vite-plugin-svelte": "^5.1.0",
|
||||||
@ -25,16 +27,16 @@
|
|||||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||||
"@typescript-eslint/parser": "^8.0.0",
|
"@typescript-eslint/parser": "^8.0.0",
|
||||||
"@zerodevx/svelte-toast": "^0.9.3",
|
"@zerodevx/svelte-toast": "^0.9.3",
|
||||||
"eslint": "^9.11.1",
|
"eslint": "^9.30.1",
|
||||||
"eslint-config-prettier": "^10.0.0",
|
"eslint-config-prettier": "^10.0.0",
|
||||||
"eslint-plugin-svelte": "^3.0.0",
|
"eslint-plugin-svelte": "^3.0.0",
|
||||||
|
"globals": "^16.3.0",
|
||||||
"prettier": "^3.1.0",
|
"prettier": "^3.1.0",
|
||||||
"prettier-plugin-svelte": "^3.2.6",
|
"prettier-plugin-svelte": "^3.2.6",
|
||||||
"svelte": "^5",
|
"svelte": "^5",
|
||||||
"svelte-check": "^4.0.0",
|
"svelte-check": "^4.0.0",
|
||||||
"tslib": "^2.0.0",
|
"tslib": "^2.0.0",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0"
|
||||||
"vite": "^6.0.0"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.0.0"
|
"node": ">=20.0.0"
|
||||||
@ -77,6 +79,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"aix"
|
"aix"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -94,6 +97,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"android"
|
"android"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -111,6 +115,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"android"
|
"android"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -128,6 +133,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"android"
|
"android"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -145,6 +151,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -162,6 +169,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -179,6 +187,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"freebsd"
|
"freebsd"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -196,6 +205,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"freebsd"
|
"freebsd"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -213,6 +223,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -230,6 +241,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -247,6 +259,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -264,6 +277,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -281,6 +295,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -298,6 +313,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -315,6 +331,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -332,6 +349,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -349,6 +367,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -366,6 +385,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"netbsd"
|
"netbsd"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -383,6 +403,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"netbsd"
|
"netbsd"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -400,6 +421,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"openbsd"
|
"openbsd"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -417,6 +439,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"openbsd"
|
"openbsd"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -434,6 +457,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"sunos"
|
"sunos"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -451,6 +475,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -468,6 +493,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -485,6 +511,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@ -533,30 +560,6 @@
|
|||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/config-array/node_modules/brace-expansion": {
|
|
||||||
"version": "1.1.12",
|
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
|
||||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"balanced-match": "^1.0.0",
|
|
||||||
"concat-map": "0.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@eslint/config-array/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,
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"brace-expansion": "^1.1.7"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@eslint/config-helpers": {
|
"node_modules/@eslint/config-helpers": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz",
|
||||||
@ -604,44 +607,23 @@
|
|||||||
"url": "https://opencollective.com/eslint"
|
"url": "https://opencollective.com/eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
|
"node_modules/@eslint/eslintrc/node_modules/globals": {
|
||||||
"version": "1.1.12",
|
"version": "14.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
|
||||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"balanced-match": "^1.0.0",
|
|
||||||
"concat-map": "0.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@eslint/eslintrc/node_modules/ignore": {
|
|
||||||
"version": "5.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
|
||||||
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
|
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 4"
|
"node": ">=18"
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/eslintrc/node_modules/minimatch": {
|
"funding": {
|
||||||
"version": "3.1.2",
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
|
||||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"brace-expansion": "^1.1.7"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "9.30.0",
|
"version": "9.30.1",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz",
|
||||||
"integrity": "sha512-Wzw3wQwPvc9sHM+NjakWTcPx11mbZyiYHuwWa/QfZ7cIRX7WK54PSk7bdyXDaoaopUcMatv1zaQvOAAO8hCdww==",
|
"integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -1158,9 +1140,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/gen-mapping": {
|
"node_modules/@jridgewell/gen-mapping": {
|
||||||
"version": "0.3.11",
|
"version": "0.3.12",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.11.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz",
|
||||||
"integrity": "sha512-C512c1ytBTio4MrpWKlJpyFHT6+qfFL8SZ58zBzJ1OOzUEjHeF1BtjY2fH7n4x/g2OV/KiiMLAivOp1DXmiMMw==",
|
"integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -1179,16 +1161,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/sourcemap-codec": {
|
"node_modules/@jridgewell/sourcemap-codec": {
|
||||||
"version": "1.5.3",
|
"version": "1.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz",
|
||||||
"integrity": "sha512-AiR5uKpFxP3PjO4R19kQGIMwxyRyPuXmKEEy301V1C0+1rVjS94EZQXf1QKZYN8Q0YM+estSPhmx5JwNftv6nw==",
|
"integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/trace-mapping": {
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
"version": "0.3.28",
|
"version": "0.3.29",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.28.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz",
|
||||||
"integrity": "sha512-KNNHHwW3EIp4EDYOvYFGyIFfx36R2dNJYH4knnZlF8T5jdbD5Wx8xmSaQ2gP9URkJ04LGEtlcCtwArKcmFcwKw==",
|
"integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -1364,9 +1346,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
"version": "4.44.1",
|
"version": "4.44.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.2.tgz",
|
||||||
"integrity": "sha512-JAcBr1+fgqx20m7Fwe1DxPUl/hPkee6jA6Pl7n1v2EFiktAHenTaXl5aIFjUIEsfn9w3HE4gK1lEgNGMzBDs1w==",
|
"integrity": "sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -1378,9 +1360,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm64": {
|
"node_modules/@rollup/rollup-android-arm64": {
|
||||||
"version": "4.44.1",
|
"version": "4.44.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.2.tgz",
|
||||||
"integrity": "sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ==",
|
"integrity": "sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -1392,9 +1374,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||||
"version": "4.44.1",
|
"version": "4.44.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.2.tgz",
|
||||||
"integrity": "sha512-fM/xPesi7g2M7chk37LOnmnSTHLG/v2ggWqKj3CCA1rMA4mm5KVBT1fNoswbo1JhPuNNZrVwpTvlCVggv8A2zg==",
|
"integrity": "sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -1406,9 +1388,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-x64": {
|
"node_modules/@rollup/rollup-darwin-x64": {
|
||||||
"version": "4.44.1",
|
"version": "4.44.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.2.tgz",
|
||||||
"integrity": "sha512-gDnWk57urJrkrHQ2WVx9TSVTH7lSlU7E3AFqiko+bgjlh78aJ88/3nycMax52VIVjIm3ObXnDL2H00e/xzoipw==",
|
"integrity": "sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -1420,9 +1402,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||||
"version": "4.44.1",
|
"version": "4.44.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.2.tgz",
|
||||||
"integrity": "sha512-wnFQmJ/zPThM5zEGcnDcCJeYJgtSLjh1d//WuHzhf6zT3Md1BvvhJnWoy+HECKu2bMxaIcfWiu3bJgx6z4g2XA==",
|
"integrity": "sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -1434,9 +1416,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||||
"version": "4.44.1",
|
"version": "4.44.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.2.tgz",
|
||||||
"integrity": "sha512-uBmIxoJ4493YATvU2c0upGz87f99e3wop7TJgOA/bXMFd2SvKCI7xkxY/5k50bv7J6dw1SXT4MQBQSLn8Bb/Uw==",
|
"integrity": "sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -1448,9 +1430,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||||
"version": "4.44.1",
|
"version": "4.44.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.2.tgz",
|
||||||
"integrity": "sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ==",
|
"integrity": "sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -1462,9 +1444,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||||
"version": "4.44.1",
|
"version": "4.44.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.2.tgz",
|
||||||
"integrity": "sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw==",
|
"integrity": "sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -1476,9 +1458,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||||
"version": "4.44.1",
|
"version": "4.44.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.2.tgz",
|
||||||
"integrity": "sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ==",
|
"integrity": "sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -1490,9 +1472,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||||
"version": "4.44.1",
|
"version": "4.44.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.2.tgz",
|
||||||
"integrity": "sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g==",
|
"integrity": "sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -1504,9 +1486,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
||||||
"version": "4.44.1",
|
"version": "4.44.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.2.tgz",
|
||||||
"integrity": "sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew==",
|
"integrity": "sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
@ -1518,9 +1500,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||||
"version": "4.44.1",
|
"version": "4.44.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.2.tgz",
|
||||||
"integrity": "sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA==",
|
"integrity": "sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@ -1532,9 +1514,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||||
"version": "4.44.1",
|
"version": "4.44.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.2.tgz",
|
||||||
"integrity": "sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw==",
|
"integrity": "sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@ -1546,9 +1528,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||||
"version": "4.44.1",
|
"version": "4.44.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.2.tgz",
|
||||||
"integrity": "sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg==",
|
"integrity": "sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@ -1560,9 +1542,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||||
"version": "4.44.1",
|
"version": "4.44.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.2.tgz",
|
||||||
"integrity": "sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw==",
|
"integrity": "sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@ -1574,9 +1556,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||||
"version": "4.44.1",
|
"version": "4.44.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.2.tgz",
|
||||||
"integrity": "sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw==",
|
"integrity": "sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -1588,9 +1570,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||||
"version": "4.44.1",
|
"version": "4.44.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.2.tgz",
|
||||||
"integrity": "sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g==",
|
"integrity": "sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -1602,9 +1584,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||||
"version": "4.44.1",
|
"version": "4.44.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.2.tgz",
|
||||||
"integrity": "sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg==",
|
"integrity": "sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -1616,9 +1598,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||||
"version": "4.44.1",
|
"version": "4.44.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.2.tgz",
|
||||||
"integrity": "sha512-JYA3qvCOLXSsnTR3oiyGws1Dm0YTuxAAeaYGVlGpUsHqloPcFjPg+X0Fj2qODGLNwQOAcCiQmHub/V007kiH5A==",
|
"integrity": "sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@ -1630,9 +1612,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||||
"version": "4.44.1",
|
"version": "4.44.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.2.tgz",
|
||||||
"integrity": "sha512-J8o22LuF0kTe7m+8PvW9wk3/bRq5+mRo5Dqo6+vXb7otCm3TPhYOJqOaQtGU9YMWQSL3krMnoOxMr0+9E6F3Ug==",
|
"integrity": "sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -1774,9 +1756,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "22.15.34",
|
"version": "22.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.0.tgz",
|
||||||
"integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==",
|
"integrity": "sha512-B2egV9wALML1JCpv3VQoQ+yesQKAmNMBIAY7OteVrikcOcAkWm+dGL6qpeCktPjAv6N1JLnhbNiqS35UpFyBsQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -1840,6 +1822,16 @@
|
|||||||
"typescript": ">=4.8.4 <5.9.0"
|
"typescript": ">=4.8.4 <5.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
|
||||||
|
"version": "7.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
|
||||||
|
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "8.35.1",
|
"version": "8.35.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.1.tgz",
|
||||||
@ -1989,6 +1981,32 @@
|
|||||||
"typescript": ">=4.8.4 <5.9.0"
|
"typescript": ">=4.8.4 <5.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
|
||||||
|
"version": "9.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||||
|
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16 || 14 >=14.17"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@typescript-eslint/utils": {
|
"node_modules/@typescript-eslint/utils": {
|
||||||
"version": "8.35.1",
|
"version": "8.35.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.1.tgz",
|
||||||
@ -2264,13 +2282,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "2.0.2",
|
"version": "1.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^1.0.0"
|
"balanced-match": "^1.0.0",
|
||||||
|
"concat-map": "0.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/braces": {
|
"node_modules/braces": {
|
||||||
@ -2605,9 +2624,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/dotenv": {
|
"node_modules/dotenv": {
|
||||||
"version": "17.0.0",
|
"version": "17.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.0.1.tgz",
|
||||||
"integrity": "sha512-A0BJ5lrpJVSfnMMXjmeO0xUnoxqsBHWCoqqTnGwGYVdnctqXXUEhJOO7LxmgxJon9tEZFGpe0xPRX0h2v3AANQ==",
|
"integrity": "sha512-GLjkduuAL7IMJg/ZnOPm9AnWKJ82mSE2tzXLaJ/6hD6DhwGfZaXG77oB8qbReyiczNxnbxQKyh0OE5mXq0bAHA==",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
@ -2666,6 +2685,7 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"esbuild": "bin/esbuild"
|
"esbuild": "bin/esbuild"
|
||||||
},
|
},
|
||||||
@ -2714,9 +2734,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "9.30.0",
|
"version": "9.30.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz",
|
||||||
"integrity": "sha512-iN/SiPxmQu6EVkf+m1qpBxzUhE12YqFLOSySuOyVLJLEF9nzTf+h/1AJYc1JWzCnktggeNrjvQGLngDzXirU6g==",
|
"integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -2726,7 +2746,7 @@
|
|||||||
"@eslint/config-helpers": "^0.3.0",
|
"@eslint/config-helpers": "^0.3.0",
|
||||||
"@eslint/core": "^0.14.0",
|
"@eslint/core": "^0.14.0",
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@eslint/js": "9.30.0",
|
"@eslint/js": "9.30.1",
|
||||||
"@eslint/plugin-kit": "^0.3.1",
|
"@eslint/plugin-kit": "^0.3.1",
|
||||||
"@humanfs/node": "^0.16.6",
|
"@humanfs/node": "^0.16.6",
|
||||||
"@humanwhocodes/module-importer": "^1.0.1",
|
"@humanwhocodes/module-importer": "^1.0.1",
|
||||||
@ -2824,19 +2844,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-svelte/node_modules/globals": {
|
|
||||||
"version": "16.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz",
|
|
||||||
"integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/eslint-scope": {
|
"node_modules/eslint-scope": {
|
||||||
"version": "8.4.0",
|
"version": "8.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
|
||||||
@ -2867,17 +2874,6 @@
|
|||||||
"url": "https://opencollective.com/eslint"
|
"url": "https://opencollective.com/eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint/node_modules/brace-expansion": {
|
|
||||||
"version": "1.1.12",
|
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
|
||||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"balanced-match": "^1.0.0",
|
|
||||||
"concat-map": "0.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/eslint/node_modules/eslint-visitor-keys": {
|
"node_modules/eslint/node_modules/eslint-visitor-keys": {
|
||||||
"version": "4.2.1",
|
"version": "4.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
|
||||||
@ -2891,29 +2887,6 @@
|
|||||||
"url": "https://opencollective.com/eslint"
|
"url": "https://opencollective.com/eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint/node_modules/ignore": {
|
|
||||||
"version": "5.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
|
||||||
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/eslint/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,
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"brace-expansion": "^1.1.7"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/esm-env": {
|
"node_modules/esm-env": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz",
|
||||||
@ -2966,9 +2939,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/esrap": {
|
"node_modules/esrap": {
|
||||||
"version": "1.4.9",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/esrap/-/esrap-1.4.9.tgz",
|
"resolved": "https://registry.npmjs.org/esrap/-/esrap-2.1.0.tgz",
|
||||||
"integrity": "sha512-3OMlcd0a03UGuZpPeUC1HxR3nA23l+HEyCiZw3b3FumJIN9KphoGzDJKMXI1S72jVS1dsenDyQC0kJlO1U9E1g==",
|
"integrity": "sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -3295,34 +3268,10 @@
|
|||||||
"node": ">=10.13.0"
|
"node": ">=10.13.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/glob/node_modules/brace-expansion": {
|
|
||||||
"version": "1.1.12",
|
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
|
||||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"balanced-match": "^1.0.0",
|
|
||||||
"concat-map": "0.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/glob/node_modules/minimatch": {
|
|
||||||
"version": "3.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
|
||||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
|
||||||
"license": "ISC",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"brace-expansion": "^1.1.7"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/globals": {
|
"node_modules/globals": {
|
||||||
"version": "14.0.0",
|
"version": "16.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz",
|
||||||
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
|
"integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -3456,9 +3405,9 @@
|
|||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
"node_modules/ignore": {
|
"node_modules/ignore": {
|
||||||
"version": "7.0.5",
|
"version": "5.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
||||||
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
|
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -3866,19 +3815,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/minimatch": {
|
"node_modules/minimatch": {
|
||||||
"version": "9.0.5",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"brace-expansion": "^2.0.1"
|
"brace-expansion": "^1.1.7"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16 || 14 >=14.17"
|
"node": "*"
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/minimist": {
|
"node_modules/minimist": {
|
||||||
@ -4677,9 +4623,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.44.1",
|
"version": "4.44.2",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.2.tgz",
|
||||||
"integrity": "sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg==",
|
"integrity": "sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -4693,26 +4639,26 @@
|
|||||||
"npm": ">=8.0.0"
|
"npm": ">=8.0.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rollup/rollup-android-arm-eabi": "4.44.1",
|
"@rollup/rollup-android-arm-eabi": "4.44.2",
|
||||||
"@rollup/rollup-android-arm64": "4.44.1",
|
"@rollup/rollup-android-arm64": "4.44.2",
|
||||||
"@rollup/rollup-darwin-arm64": "4.44.1",
|
"@rollup/rollup-darwin-arm64": "4.44.2",
|
||||||
"@rollup/rollup-darwin-x64": "4.44.1",
|
"@rollup/rollup-darwin-x64": "4.44.2",
|
||||||
"@rollup/rollup-freebsd-arm64": "4.44.1",
|
"@rollup/rollup-freebsd-arm64": "4.44.2",
|
||||||
"@rollup/rollup-freebsd-x64": "4.44.1",
|
"@rollup/rollup-freebsd-x64": "4.44.2",
|
||||||
"@rollup/rollup-linux-arm-gnueabihf": "4.44.1",
|
"@rollup/rollup-linux-arm-gnueabihf": "4.44.2",
|
||||||
"@rollup/rollup-linux-arm-musleabihf": "4.44.1",
|
"@rollup/rollup-linux-arm-musleabihf": "4.44.2",
|
||||||
"@rollup/rollup-linux-arm64-gnu": "4.44.1",
|
"@rollup/rollup-linux-arm64-gnu": "4.44.2",
|
||||||
"@rollup/rollup-linux-arm64-musl": "4.44.1",
|
"@rollup/rollup-linux-arm64-musl": "4.44.2",
|
||||||
"@rollup/rollup-linux-loongarch64-gnu": "4.44.1",
|
"@rollup/rollup-linux-loongarch64-gnu": "4.44.2",
|
||||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.44.1",
|
"@rollup/rollup-linux-powerpc64le-gnu": "4.44.2",
|
||||||
"@rollup/rollup-linux-riscv64-gnu": "4.44.1",
|
"@rollup/rollup-linux-riscv64-gnu": "4.44.2",
|
||||||
"@rollup/rollup-linux-riscv64-musl": "4.44.1",
|
"@rollup/rollup-linux-riscv64-musl": "4.44.2",
|
||||||
"@rollup/rollup-linux-s390x-gnu": "4.44.1",
|
"@rollup/rollup-linux-s390x-gnu": "4.44.2",
|
||||||
"@rollup/rollup-linux-x64-gnu": "4.44.1",
|
"@rollup/rollup-linux-x64-gnu": "4.44.2",
|
||||||
"@rollup/rollup-linux-x64-musl": "4.44.1",
|
"@rollup/rollup-linux-x64-musl": "4.44.2",
|
||||||
"@rollup/rollup-win32-arm64-msvc": "4.44.1",
|
"@rollup/rollup-win32-arm64-msvc": "4.44.2",
|
||||||
"@rollup/rollup-win32-ia32-msvc": "4.44.1",
|
"@rollup/rollup-win32-ia32-msvc": "4.44.2",
|
||||||
"@rollup/rollup-win32-x64-msvc": "4.44.1",
|
"@rollup/rollup-win32-x64-msvc": "4.44.2",
|
||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -5124,9 +5070,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/svelte": {
|
"node_modules/svelte": {
|
||||||
"version": "5.34.9",
|
"version": "5.35.2",
|
||||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.34.9.tgz",
|
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.35.2.tgz",
|
||||||
"integrity": "sha512-sld35zFpooaSRSj4qw8Vl/cyyK0/sLQq9qhJ7BGZo/Kd0ggYtEnvNYLlzhhoqYsYQzA0hJqkzt3RBO/8KoTZOg==",
|
"integrity": "sha512-uW/rRXYrhZ7Dh4UQNZ0t+oVGL1dEM+95GavCO8afAk1IY2cPq9BcZv9C3um5aLIya2y8lIeLPxLII9ASGg9Dzw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -5139,7 +5085,7 @@
|
|||||||
"axobject-query": "^4.1.0",
|
"axobject-query": "^4.1.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"esm-env": "^1.2.1",
|
"esm-env": "^1.2.1",
|
||||||
"esrap": "^1.4.8",
|
"esrap": "^2.1.0",
|
||||||
"is-reference": "^3.0.3",
|
"is-reference": "^3.0.3",
|
||||||
"locate-character": "^3.0.0",
|
"locate-character": "^3.0.0",
|
||||||
"magic-string": "^0.30.11",
|
"magic-string": "^0.30.11",
|
||||||
@ -5291,6 +5237,7 @@
|
|||||||
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
|
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fdir": "^6.4.4",
|
"fdir": "^6.4.4",
|
||||||
"picomatch": "^4.0.2"
|
"picomatch": "^4.0.2"
|
||||||
@ -5433,6 +5380,7 @@
|
|||||||
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
|
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"fdir": "^6.4.4",
|
"fdir": "^6.4.4",
|
||||||
@ -5503,14 +5451,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vitefu": {
|
"node_modules/vitefu": {
|
||||||
"version": "1.0.7",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.1.tgz",
|
||||||
"integrity": "sha512-eRWXLBbJjW3X5z5P5IHcSm2yYbYRPb2kQuc+oqsbAl99WB5kVsPbiiox+cymo8twTzifA6itvhr2CmjnaZZp0Q==",
|
"integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"tests/deps/*",
|
"tests/deps/*",
|
||||||
"tests/projects/*"
|
"tests/projects/*",
|
||||||
|
"tests/projects/workspace/packages/*"
|
||||||
],
|
],
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0"
|
"vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0"
|
||||||
|
10
package.json
10
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "moshing-mammut",
|
"name": "moshing-mammut",
|
||||||
"version": "2.0.0",
|
"version": "2.0.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "LGPL-3.0-or-later",
|
"license": "LGPL-3.0-or-later",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -14,6 +14,8 @@
|
|||||||
"format": "prettier --plugin-search-dir . --write ."
|
"format": "prettier --plugin-search-dir . --write ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
|
"@eslint/js": "^9.30.1",
|
||||||
"@sveltejs/adapter-node": "^5.2.12",
|
"@sveltejs/adapter-node": "^5.2.12",
|
||||||
"@sveltejs/kit": "^2.22.2",
|
"@sveltejs/kit": "^2.22.2",
|
||||||
"@sveltejs/vite-plugin-svelte": "^5.1.0",
|
"@sveltejs/vite-plugin-svelte": "^5.1.0",
|
||||||
@ -23,16 +25,16 @@
|
|||||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||||
"@typescript-eslint/parser": "^8.0.0",
|
"@typescript-eslint/parser": "^8.0.0",
|
||||||
"@zerodevx/svelte-toast": "^0.9.3",
|
"@zerodevx/svelte-toast": "^0.9.3",
|
||||||
"eslint": "^9.11.1",
|
"eslint": "^9.30.1",
|
||||||
"eslint-config-prettier": "^10.0.0",
|
"eslint-config-prettier": "^10.0.0",
|
||||||
"eslint-plugin-svelte": "^3.0.0",
|
"eslint-plugin-svelte": "^3.0.0",
|
||||||
|
"globals": "^16.3.0",
|
||||||
"prettier": "^3.1.0",
|
"prettier": "^3.1.0",
|
||||||
"prettier-plugin-svelte": "^3.2.6",
|
"prettier-plugin-svelte": "^3.2.6",
|
||||||
"svelte": "^5",
|
"svelte": "^5",
|
||||||
"svelte-check": "^4.0.0",
|
"svelte-check": "^4.0.0",
|
||||||
"tslib": "^2.0.0",
|
"tslib": "^2.0.0",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0"
|
||||||
"vite": "^6.0.0"
|
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -3,14 +3,32 @@ import { TimelineReader } from '$lib/server/timeline';
|
|||||||
import type { Handle, HandleServerError } from '@sveltejs/kit';
|
import type { Handle, HandleServerError } from '@sveltejs/kit';
|
||||||
import { error } from '@sveltejs/kit';
|
import { error } from '@sveltejs/kit';
|
||||||
import fs from 'fs/promises';
|
import fs from 'fs/promises';
|
||||||
|
import { close } from '$lib/server/db';
|
||||||
|
import { version } from '$app/environment';
|
||||||
|
|
||||||
const logger = new Logger('App');
|
const logger = new Logger('App');
|
||||||
|
|
||||||
logger.log('App startup');
|
if (process?.pid) {
|
||||||
|
try {
|
||||||
|
await fs.writeFile('moshing-mammut.pid', process.pid.toString());
|
||||||
|
} catch (e) {
|
||||||
|
logger.error('Could not write PID to file', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log('App startup, version', version, 'PID', process?.pid);
|
||||||
|
|
||||||
|
logger.log('Debug log enabled', Logger.isDebugEnabled());
|
||||||
TimelineReader.init();
|
TimelineReader.init();
|
||||||
|
|
||||||
export const handleError = (({ error }) => {
|
process.on('sveltekit:shutdown', (reason) => {
|
||||||
if (error instanceof Error) {
|
close();
|
||||||
|
logger.log('Shutting down', reason);
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const handleError = (({ error, status }) => {
|
||||||
|
if (error instanceof Error && status !== 404) {
|
||||||
logger.error('Something went wrong:', error.name, error.message);
|
logger.error('Something went wrong:', error.name, error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,12 +38,6 @@ export const handleError = (({ error }) => {
|
|||||||
}) satisfies HandleServerError;
|
}) satisfies HandleServerError;
|
||||||
|
|
||||||
export const handle = (async ({ event, resolve }) => {
|
export const handle = (async ({ event, resolve }) => {
|
||||||
const searchParams = event.url.searchParams;
|
|
||||||
const authCode = searchParams.get('code');
|
|
||||||
if (authCode) {
|
|
||||||
logger.debug('received GET hook', event.url.searchParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reeder *insists* on checking /feed instead of /feed.xml
|
// Reeder *insists* on checking /feed instead of /feed.xml
|
||||||
if (event.url.pathname === '/feed') {
|
if (event.url.pathname === '/feed') {
|
||||||
return new Response('', { status: 301, headers: { Location: '/feed.xml' } });
|
return new Response('', { status: 301, headers: { Location: '/feed.xml' } });
|
||||||
|
1
src/lib/assets/spotify-fill.svg
Normal file
1
src/lib/assets/spotify-fill.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M12.001 2C6.50098 2 2.00098 6.5 2.00098 12C2.00098 17.5 6.50098 22 12.001 22C17.501 22 22.001 17.5 22.001 12C22.001 6.5 17.551 2 12.001 2ZM15.751 16.65C13.401 15.2 10.451 14.8992 6.95014 15.6992C6.60181 15.8008 6.30098 15.55 6.20098 15.25C6.10098 14.8992 6.35098 14.6 6.65098 14.5C10.451 13.6492 13.751 14 16.351 15.6C16.701 15.75 16.7501 16.1492 16.6018 16.45C16.4018 16.7492 16.0518 16.85 15.751 16.65ZM16.7501 13.95C14.051 12.3 9.95098 11.8 6.80098 12.8C6.40181 12.9 5.95098 12.7 5.85098 12.3C5.75098 11.9 5.95098 11.4492 6.35098 11.3492C10.001 10.25 14.501 10.8008 17.601 12.7C17.9018 12.8508 18.051 13.35 17.8018 13.7C17.551 14.05 17.101 14.2 16.7501 13.95ZM6.30098 9.75083C5.80098 9.9 5.30098 9.6 5.15098 9.15C5.00098 8.64917 5.30098 8.15 5.75098 7.99917C9.30098 6.94917 15.151 7.14917 18.8518 9.35C19.301 9.6 19.451 10.2 19.201 10.65C18.9518 11.0008 18.351 11.1492 17.9018 10.9C14.701 9 9.35098 8.8 6.30098 9.75083Z"></path></svg>
|
After Width: | Height: | Size: 1.0 KiB |
19
src/lib/assets/tidal.svg
Normal file
19
src/lib/assets/tidal.svg
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(0.98352,0,0,1,0.395532,0)">
|
||||||
|
<rect x="-0.402" y="0" width="24.402" height="24" style="fill-opacity:0;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.755537,0,0,0.755537,0,0.100656)">
|
||||||
|
<path d="M21.177,5.705L15.883,10.999L10.589,5.705L15.883,0.412L21.177,5.705Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.755537,0,0,0.755537,0,0.100656)">
|
||||||
|
<path d="M21.177,16.294L15.883,21.588L10.589,16.294L15.883,10.999L21.177,16.294Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.755537,0,0,0.755537,0,0.100656)">
|
||||||
|
<path d="M10.589,5.705L5.294,11L0,5.705L5.294,0.412L10.589,5.705Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.755537,0,0,0.755537,0,0.100656)">
|
||||||
|
<path d="M31.766,5.705L26.472,11L21.177,5.705L26.472,0.412L31.766,5.705Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
1
src/lib/assets/youtube-fill.svg
Normal file
1
src/lib/assets/youtube-fill.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M12.2439 4C12.778 4.00294 14.1143 4.01586 15.5341 4.07273L16.0375 4.09468C17.467 4.16236 18.8953 4.27798 19.6037 4.4755C20.5486 4.74095 21.2913 5.5155 21.5423 6.49732C21.942 8.05641 21.992 11.0994 21.9982 11.8358L21.9991 11.9884L21.9991 11.9991C21.9991 11.9991 21.9991 12.0028 21.9991 12.0099L21.9982 12.1625C21.992 12.8989 21.942 15.9419 21.5423 17.501C21.2878 18.4864 20.5451 19.261 19.6037 19.5228C18.8953 19.7203 17.467 19.8359 16.0375 19.9036L15.5341 19.9255C14.1143 19.9824 12.778 19.9953 12.2439 19.9983L12.0095 19.9991L11.9991 19.9991C11.9991 19.9991 11.9956 19.9991 11.9887 19.9991L11.7545 19.9983C10.6241 19.9921 5.89772 19.941 4.39451 19.5228C3.4496 19.2573 2.70692 18.4828 2.45587 17.501C2.0562 15.9419 2.00624 12.8989 2 12.1625V11.8358C2.00624 11.0994 2.0562 8.05641 2.45587 6.49732C2.7104 5.51186 3.45308 4.73732 4.39451 4.4755C5.89772 4.05723 10.6241 4.00622 11.7545 4H12.2439ZM9.99911 8.49914V15.4991L15.9991 11.9991L9.99911 8.49914Z"></path></svg>
|
After Width: | Height: | Size: 1.0 KiB |
@ -1,6 +1,10 @@
|
|||||||
<script>
|
<script>
|
||||||
import git from '$lib/assets/git-branch-fill.svg';
|
import git from '$lib/assets/git-branch-fill.svg';
|
||||||
import rss from '$lib/assets/rss-fill.svg';
|
import rss from '$lib/assets/rss-fill.svg';
|
||||||
|
import spotify from '$lib/assets/spotify-fill.svg';
|
||||||
|
import youtube from '$lib/assets/youtube-fill.svg';
|
||||||
|
import tidal from '$lib/assets/tidal.svg';
|
||||||
|
import { version } from '$app/environment';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
@ -16,7 +20,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<a href="https://phlaym.net/git/phlaym/moshing-mammut">
|
<a href="https://phlaym.net/git/phlaym/moshing-mammut">
|
||||||
<img alt="Git branch" src={git} class="icon" />
|
<img alt="Git branch" src={git} class="icon" />
|
||||||
<span class="label">Source Code</span>
|
<span class="label"><span class="feedSuffix">Source Code </span>v{version}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
|
||||||
@ -26,6 +30,30 @@
|
|||||||
<span class="label">RSS<span class="feedSuffix"> Feed</span></span>
|
<span class="label">RSS<span class="feedSuffix"> Feed</span></span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
<div>
|
||||||
|
<a href="https://open.spotify.com/playlist/62B8GOmJE3YrASAXSQVRVU" target="_blank">
|
||||||
|
<img alt="Spotify" src={spotify} class="icon" />
|
||||||
|
<span class="label">Spotify</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
|
||||||
|
<div>
|
||||||
|
<a
|
||||||
|
href="https://www.youtube.com/playlist?list=PLrSjNPaM6N4S54jT5R-ebKAYLBIEDb8sX"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<img alt="Youtube" src={youtube} class="icon" />
|
||||||
|
<span class="label">Youtube</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
|
||||||
|
<div>
|
||||||
|
<a href="https://tidal.com/playlist/9f60278a-7b9b-459b-b7e5-65a7849fe498" target="_blank">
|
||||||
|
<img alt="Tidal" src={tidal} class="icon" />
|
||||||
|
<span class="label">Tidal</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -131,11 +131,10 @@
|
|||||||
</picture>
|
</picture>
|
||||||
<a href={song.pageUrl ?? song.postedUrl} target="_blank">
|
<a href={song.pageUrl ?? song.postedUrl} target="_blank">
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<picture>
|
<picture class="cover">
|
||||||
{@html getSourceSetHtml(song)}
|
{@html getSourceSetHtml(song)}
|
||||||
<img
|
<img
|
||||||
src={song.thumbnailUrl}
|
src={song.thumbnailUrl}
|
||||||
class="cover"
|
|
||||||
alt="Cover for {song.artistName} - {song.title}"
|
alt="Cover for {song.artistName} - {song.title}"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
width={song.thumbnailWidth}
|
width={song.thumbnailWidth}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
import { DEBUG_LOG } from '$env/static/private';
|
||||||
import { env } from '$env/dynamic/private';
|
import { env } from '$env/dynamic/private';
|
||||||
import { isTruthy } from '$lib/truthyString';
|
import { isTruthy } from '$lib/truthyString';
|
||||||
const { DEV } = import.meta.env;
|
const { DEV } = import.meta.env;
|
||||||
|
|
||||||
export const enableVerboseLog = isTruthy(env.VERBOSE);
|
export const enableVerboseLog = isTruthy(env.VERBOSE);
|
||||||
|
export const debugLogEnv = isTruthy(DEBUG_LOG);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Use the new {@link Logger} class instead.
|
* @deprecated Use the new {@link Logger} class instead.
|
||||||
@ -41,31 +43,31 @@ export class Logger {
|
|||||||
public constructor(private name: string) {}
|
public constructor(private name: string) {}
|
||||||
|
|
||||||
public static isDebugEnabled(): boolean {
|
public static isDebugEnabled(): boolean {
|
||||||
return DEV;
|
return debugLogEnv || DEV || enableVerboseLog;
|
||||||
}
|
}
|
||||||
public verbose(...params: any[]) {
|
public verbose(...params: any[]) {
|
||||||
if (!enableVerboseLog) {
|
if (!enableVerboseLog) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.debug(new Date().toISOString(), `- ${this.name} -`, ...params);
|
console.debug(new Date().toISOString(), '- [VRBSE]', `- ${this.name} -`, ...params);
|
||||||
}
|
}
|
||||||
public debug(...params: any[]) {
|
public debug(...params: any[]) {
|
||||||
if (!Logger.isDebugEnabled()) {
|
if (!Logger.isDebugEnabled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.debug(new Date().toISOString(), `- ${this.name} -`, ...params);
|
console.debug(new Date().toISOString(), '- [DEBUG]', `- ${this.name} -`, ...params);
|
||||||
}
|
}
|
||||||
public log(...params: any[]) {
|
public log(...params: any[]) {
|
||||||
console.log(new Date().toISOString(), `- ${this.name} -`, ...params);
|
console.log(new Date().toISOString(), '- [ LOG ]', `- ${this.name} -`, ...params);
|
||||||
}
|
}
|
||||||
public info(...params: any[]) {
|
public info(...params: any[]) {
|
||||||
console.info(new Date().toISOString(), `- ${this.name} -`, ...params);
|
console.info(new Date().toISOString(), '- [INFO ]', `- ${this.name} -`, ...params);
|
||||||
}
|
}
|
||||||
public warn(...params: any[]) {
|
public warn(...params: any[]) {
|
||||||
console.warn(new Date().toISOString(), `- ${this.name} -`, ...params);
|
console.warn(new Date().toISOString(), '- [WARN ]', `- ${this.name} -`, ...params);
|
||||||
}
|
}
|
||||||
public error(...params: any[]) {
|
public error(...params: any[]) {
|
||||||
console.error(new Date().toISOString(), `- ${this.name} -`, ...params);
|
console.error(new Date().toISOString(), '- [ERROR]', `- ${this.name} -`, ...params);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static error(...params: any[]) {
|
public static error(...params: any[]) {
|
||||||
|
@ -5,6 +5,7 @@ export type SongInfo = {
|
|||||||
youtubeUrl?: string;
|
youtubeUrl?: string;
|
||||||
spotifyUrl?: string;
|
spotifyUrl?: string;
|
||||||
spotifyUri?: string;
|
spotifyUri?: string;
|
||||||
|
tidalUri?: string;
|
||||||
type: 'song' | 'album';
|
type: 'song' | 'album';
|
||||||
title?: string;
|
title?: string;
|
||||||
artistName?: string;
|
artistName?: string;
|
||||||
|
@ -68,6 +68,15 @@ type Migration = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const db: sqlite3.Database = new sqlite3.Database('moshingmammut.db');
|
const db: sqlite3.Database = new sqlite3.Database('moshingmammut.db');
|
||||||
|
|
||||||
|
export function close() {
|
||||||
|
try {
|
||||||
|
db.close();
|
||||||
|
} catch (e) {
|
||||||
|
logger.error('Could not close DB');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// for the local masto instance, the instance name is *not* saved
|
// for the local masto instance, the instance name is *not* saved
|
||||||
// as part of the username or acct, so it needs to be stripped
|
// as part of the username or acct, so it needs to be stripped
|
||||||
const ignoredUsers: string[] =
|
const ignoredUsers: string[] =
|
||||||
@ -126,7 +135,7 @@ async function applyMigration(migration: Migration) {
|
|||||||
`Fetching songs for existing post ${current.toString().padStart(4, '0')} of ${total}`,
|
`Fetching songs for existing post ${current.toString().padStart(4, '0')} of ${total}`,
|
||||||
post.url
|
post.url
|
||||||
);
|
);
|
||||||
const songs = await TimelineReader.getSongInfoInPost(post);
|
const songs = await TimelineReader.instance.getSongInfoInPost(post);
|
||||||
await saveSongInfoData(post.url, songs);
|
await saveSongInfoData(post.url, songs);
|
||||||
logger.debug(`Fetched ${songs.length} songs for existing post`, post.url);
|
logger.debug(`Fetched ${songs.length} songs for existing post`, post.url);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { BASE_URL } from '$env/static/private';
|
||||||
import { Logger } from '$lib/log';
|
import { Logger } from '$lib/log';
|
||||||
import type { OauthResponse } from '$lib/mastodon/response';
|
import type { OauthResponse } from '$lib/mastodon/response';
|
||||||
import fs from 'fs/promises';
|
import fs from 'fs/promises';
|
||||||
@ -6,6 +7,7 @@ export abstract class OauthPlaylistAdder {
|
|||||||
/// How many minutes before expiry the token will be refreshed
|
/// How many minutes before expiry the token will be refreshed
|
||||||
protected refresh_time: number = 15;
|
protected refresh_time: number = 15;
|
||||||
protected logger: Logger = new Logger('OauthPlaylistAdder');
|
protected logger: Logger = new Logger('OauthPlaylistAdder');
|
||||||
|
protected redirectUri?: URL;
|
||||||
|
|
||||||
protected constructor(
|
protected constructor(
|
||||||
protected apiBase: string,
|
protected apiBase: string,
|
||||||
@ -14,15 +16,20 @@ export abstract class OauthPlaylistAdder {
|
|||||||
|
|
||||||
public async authCodeExists(): Promise<boolean> {
|
public async authCodeExists(): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const fileHandle = await fs.open(this.token_file_name);
|
const token = await this.auth();
|
||||||
await fileHandle.close();
|
return token !== null && !token.error;
|
||||||
return true;
|
|
||||||
} catch {
|
} catch {
|
||||||
this.logger.info('No auth token yet, authorizing...');
|
this.logger.info('No auth token yet, authorizing...');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected getRedirectUri(suffix: string): URL {
|
||||||
|
const uri = this.redirectUri ?? new URL(`${BASE_URL}/${suffix}`);
|
||||||
|
this.logger.debug('getRedirectUri', uri);
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
protected constructAuthUrlInternal(
|
protected constructAuthUrlInternal(
|
||||||
endpointUrl: string,
|
endpointUrl: string,
|
||||||
clientId: string,
|
clientId: string,
|
||||||
@ -45,19 +52,23 @@ export abstract class OauthPlaylistAdder {
|
|||||||
tokenUrl: URL,
|
tokenUrl: URL,
|
||||||
clientId: string,
|
clientId: string,
|
||||||
code: string,
|
code: string,
|
||||||
url: URL,
|
redirectUri: URL,
|
||||||
client_secret?: string,
|
client_secret?: string,
|
||||||
customHeader?: HeadersInit
|
customHeader?: HeadersInit,
|
||||||
|
code_verifier?: string
|
||||||
) {
|
) {
|
||||||
this.logger.debug('received code');
|
this.logger.debug('received code');
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
params.append('client_id', clientId);
|
params.append('client_id', clientId);
|
||||||
params.append('code', code);
|
params.append('code', code);
|
||||||
params.append('grant_type', 'authorization_code');
|
params.append('grant_type', 'authorization_code');
|
||||||
params.append('redirect_uri', `${url.origin}${url.pathname}`);
|
params.append('redirect_uri', redirectUri.toString());
|
||||||
if (client_secret) {
|
if (client_secret) {
|
||||||
params.append('client_secret', client_secret);
|
params.append('client_secret', client_secret);
|
||||||
}
|
}
|
||||||
|
if (code_verifier) {
|
||||||
|
params.append('code_verifier', code_verifier);
|
||||||
|
}
|
||||||
this.logger.debug('sending token req', params);
|
this.logger.debug('sending token req', params);
|
||||||
const resp: OauthResponse = await fetch(tokenUrl, {
|
const resp: OauthResponse = await fetch(tokenUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -91,12 +102,22 @@ export abstract class OauthPlaylistAdder {
|
|||||||
protected async shouldRefreshToken(): Promise<{ token: OauthResponse; refresh: boolean } | null> {
|
protected async shouldRefreshToken(): Promise<{ token: OauthResponse; refresh: boolean } | null> {
|
||||||
const token = await this.auth();
|
const token = await this.auth();
|
||||||
if (token == null || !token?.expires) {
|
if (token == null || !token?.expires) {
|
||||||
|
this.logger.warn('Cannot check if token should be refreshed. Token expiry is unreadablle');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let refreshAt = new Date();
|
if (token.error) {
|
||||||
|
this.logger.error('Access token is invalid, should refresh');
|
||||||
|
return {
|
||||||
|
token: token,
|
||||||
|
refresh: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let refreshAt = new Date(token.expires);
|
||||||
|
// Refresh token this.refresh_time minutes before it expires
|
||||||
refreshAt.setTime(refreshAt.getTime() - this.refresh_time * 60 * 1000);
|
refreshAt.setTime(refreshAt.getTime() - this.refresh_time * 60 * 1000);
|
||||||
this.logger.info('token expiry', token.expires, 'vs refresh @', refreshAt);
|
|
||||||
if (token.expires.getTime() > refreshAt.getTime()) {
|
this.logger.info('refresh @', refreshAt, 'token expires', token.expires);
|
||||||
|
if (refreshAt.getTime() > new Date().getTime()) {
|
||||||
return {
|
return {
|
||||||
token: token,
|
token: token,
|
||||||
refresh: false
|
refresh: false
|
||||||
@ -107,7 +128,7 @@ export abstract class OauthPlaylistAdder {
|
|||||||
'Token expires',
|
'Token expires',
|
||||||
token.expires,
|
token.expires,
|
||||||
token.expires.getTime(),
|
token.expires.getTime(),
|
||||||
`which is after the refresh time`,
|
`so it should be refreshed before or at`,
|
||||||
refreshAt,
|
refreshAt,
|
||||||
refreshAt.getTime()
|
refreshAt.getTime()
|
||||||
);
|
);
|
||||||
|
187
src/lib/server/playlist/tidalPlaylistAdder.ts
Normal file
187
src/lib/server/playlist/tidalPlaylistAdder.ts
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
import { TIDAL_PLAYLIST_ID, TIDAL_CLIENT_ID, TIDAL_CLIENT_SECRET } from '$env/static/private';
|
||||||
|
import { Logger } from '$lib/log';
|
||||||
|
import type { OauthResponse } from '$lib/mastodon/response';
|
||||||
|
import type { SongInfo } from '$lib/odesliResponse';
|
||||||
|
import { createHash } from 'crypto';
|
||||||
|
import { OauthPlaylistAdder } from './oauthPlaylistAdder';
|
||||||
|
import type { PlaylistAdder } from './playlistAdder';
|
||||||
|
import type { TidalAddToPlaylistResponse } from './tidalResponse';
|
||||||
|
|
||||||
|
export class TidalPlaylistAdder extends OauthPlaylistAdder implements PlaylistAdder {
|
||||||
|
private static code_verifier?: string;
|
||||||
|
|
||||||
|
public constructor() {
|
||||||
|
super('https://openapi.tidal.com/v2', 'tidal_auth_token');
|
||||||
|
//super('https://api.tidal.com/v2', 'tidal_auth_token');
|
||||||
|
this.logger = new Logger('TidalPlaylistAdder');
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructAuthUrl(redirectUri: URL): URL {
|
||||||
|
const endpoint = 'https://login.tidal.com/authorize';
|
||||||
|
const verifier = Buffer.from(crypto.getRandomValues(new Uint8Array(100)).toString(), 'ascii')
|
||||||
|
.toString('base64url')
|
||||||
|
.slice(0, 128);
|
||||||
|
const code_challenge = createHash('sha256').update(verifier).digest('base64url');
|
||||||
|
TidalPlaylistAdder.code_verifier = verifier;
|
||||||
|
|
||||||
|
let additionalParameters = new Map([
|
||||||
|
['code_challenge_method', 'S256'],
|
||||||
|
['code_challenge', code_challenge]
|
||||||
|
]);
|
||||||
|
return this.constructAuthUrlInternal(
|
||||||
|
endpoint,
|
||||||
|
TIDAL_CLIENT_ID,
|
||||||
|
'playlists.write playlists.read user.read', //r_usr w_usr
|
||||||
|
redirectUri,
|
||||||
|
additionalParameters
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async receivedAuthCode(code: string, url: URL) {
|
||||||
|
this.logger.debug('received code');
|
||||||
|
const tokenUrl = new URL('https://auth.tidal.com/v1/oauth2/token');
|
||||||
|
await this.receivedAuthCodeInternal(
|
||||||
|
tokenUrl,
|
||||||
|
TIDAL_CLIENT_ID,
|
||||||
|
code,
|
||||||
|
url,
|
||||||
|
TIDAL_CLIENT_SECRET,
|
||||||
|
undefined,
|
||||||
|
TidalPlaylistAdder.code_verifier
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async refreshToken(force: boolean = false): Promise<OauthResponse | null> {
|
||||||
|
const tokenInfo = await this.shouldRefreshToken();
|
||||||
|
if (tokenInfo == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let token = tokenInfo.token;
|
||||||
|
if (!tokenInfo.refresh && !force) {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!token.refresh_token) {
|
||||||
|
this.logger.error('Need to refresh access token, but no refresh token provided');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokenUrl = new URL('https://auth.tidal.com/v1/oauth2/token');
|
||||||
|
return await this.requestRefreshToken(tokenUrl, TIDAL_CLIENT_ID, token.refresh_token);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async addToPlaylistRetry(song: SongInfo, remaning: number = 3) {
|
||||||
|
if (remaning < 0) {
|
||||||
|
this.logger.error('max retries reached, song will not be added to playlist');
|
||||||
|
}
|
||||||
|
this.logger.debug('addToTidalPlaylist', remaning);
|
||||||
|
const token = await this.refreshToken();
|
||||||
|
if (token == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.logger.debug('token check successful');
|
||||||
|
|
||||||
|
if (!TIDAL_PLAYLIST_ID || TIDAL_PLAYLIST_ID === 'CHANGE_ME') {
|
||||||
|
this.logger.debug('no playlist ID configured');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!song.tidalUri) {
|
||||||
|
this.logger.info('Skip adding song to playlist, no Uri', song);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This would be API v2, but that's still in beta and only allows adding an item *before* another one
|
||||||
|
const options: RequestInit = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
Authorization: `${token.token_type} ${token.access_token}`,
|
||||||
|
'Content-Type': 'application/vnd.api+json',
|
||||||
|
Accept: 'application/vnd.api+json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
id: song.tidalUri,
|
||||||
|
type: 'tracks'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
meta: {
|
||||||
|
positionBefore: 'ffb6286e-237a-4dfc-bbf1-2fb0eb004ed5' // Hardcoded last element of list
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
const apiUrl = new URL(`${this.apiBase}/playlists/${TIDAL_PLAYLIST_ID}/relationships/items`);
|
||||||
|
const request = new Request(apiUrl, options);
|
||||||
|
this.logger.debug('Adding to playlist request', request);
|
||||||
|
|
||||||
|
// This would be API v1 (or api v2, but *not* the OpenAPI v2),
|
||||||
|
// but that requires r_usr and w_usr permission scopes which are impossible to request
|
||||||
|
/*
|
||||||
|
const options: RequestInit = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
Authorization: `${token.token_type} ${token.access_token}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
onArtifactNotFound: 'SKIP',
|
||||||
|
trackIds: song.tidalUri,
|
||||||
|
//toIndex: -1
|
||||||
|
onDupes: 'SKIP'
|
||||||
|
})
|
||||||
|
};
|
||||||
|
const apiUrl = new URL(`${this.apiBase}/playlists/${TIDAL_PLAYLIST_ID}/items`);
|
||||||
|
try {
|
||||||
|
const r = await fetch(new URL(`${this.apiBase}/playlists/${TIDAL_PLAYLIST_ID}`), {
|
||||||
|
headers: {
|
||||||
|
Authorization: `${token.token_type} ${token.access_token}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const txt = await r.text();
|
||||||
|
this.logger.debug('playlist', r.status, txt);
|
||||||
|
const rj = JSON.parse(txt);
|
||||||
|
this.logger.debug('playlist', rj);
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.error('playlist fetch failed', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
const request = new Request(apiUrl, options);
|
||||||
|
this.logger.debug('Adding to playlist request', request);
|
||||||
|
*/
|
||||||
|
|
||||||
|
let resp: Response | null = null;
|
||||||
|
try {
|
||||||
|
resp = await fetch(request);
|
||||||
|
let respObj: TidalAddToPlaylistResponse | null = null;
|
||||||
|
// If the request was successful, a 201 with no content is received
|
||||||
|
// Errors will have content and a different status code
|
||||||
|
if (resp.status !== 201) {
|
||||||
|
respObj = await resp.json();
|
||||||
|
}
|
||||||
|
if (respObj !== null && respObj.errors) {
|
||||||
|
this.logger.error('Add to playlist failed', song.tidalUri, resp.status, respObj.errors);
|
||||||
|
if (resp.status === 401 || respObj.errors.some((x) => x.code === 'UNAUTHORIZED')) {
|
||||||
|
const token = await this.refreshToken(true);
|
||||||
|
if (token == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.addToPlaylistRetry(song, remaning--);
|
||||||
|
}
|
||||||
|
} else if (respObj === null && resp.status === 201) {
|
||||||
|
this.logger.info('Added to playlist', song.tidalUri, song.title);
|
||||||
|
} else {
|
||||||
|
this.logger.info(
|
||||||
|
'Add to playlist result is neither 201 nor error',
|
||||||
|
song.tidalUri,
|
||||||
|
song.title,
|
||||||
|
respObj
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.error('Add to playlist request failed', resp?.status, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public async addToPlaylist(song: SongInfo) {
|
||||||
|
await this.addToPlaylistRetry(song);
|
||||||
|
}
|
||||||
|
}
|
29
src/lib/server/playlist/tidalResponse.ts
Normal file
29
src/lib/server/playlist/tidalResponse.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
export type TidalAddToPlaylistResponse = {
|
||||||
|
errors: TidalAddToPlaylistError[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TidalAddToPlaylistError = {
|
||||||
|
id: string;
|
||||||
|
status: number;
|
||||||
|
code: TidalErrorCode;
|
||||||
|
detail: string;
|
||||||
|
source: TidalAddToPlaylistErrorSource;
|
||||||
|
meta: TidalAddToPlaylistErrorMeta;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TidalAddToPlaylistErrorSource = {
|
||||||
|
parameter: string;
|
||||||
|
};
|
||||||
|
export type TidalAddToPlaylistErrorMeta = {
|
||||||
|
category: string;
|
||||||
|
};
|
||||||
|
export type TidalErrorCode =
|
||||||
|
| 'INVALID_ENUM_VALUE'
|
||||||
|
| 'VALUE_REGEX_MISMATCH'
|
||||||
|
| 'NOT_FOUND'
|
||||||
|
| 'METHOD_NOT_SUPPORTED'
|
||||||
|
| 'NOT_ACCEPTABLE'
|
||||||
|
| 'UNSUPPORTED_MEDIA_TYPE'
|
||||||
|
| 'UNAVAILABLE_FOR_LEGAL_REASONS_RESPONSE'
|
||||||
|
| 'INTERNAL_SERVER_ERROR'
|
||||||
|
| 'UNAUTHORIZED';
|
@ -1,9 +1,4 @@
|
|||||||
import {
|
import { YOUTUBE_CLIENT_ID, YOUTUBE_CLIENT_SECRET, YOUTUBE_PLAYLIST_ID } from '$env/static/private';
|
||||||
BASE_URL,
|
|
||||||
YOUTUBE_CLIENT_ID,
|
|
||||||
YOUTUBE_CLIENT_SECRET,
|
|
||||||
YOUTUBE_PLAYLIST_ID
|
|
||||||
} from '$env/static/private';
|
|
||||||
import { Logger } from '$lib/log';
|
import { Logger } from '$lib/log';
|
||||||
import type { OauthResponse } from '$lib/mastodon/response';
|
import type { OauthResponse } from '$lib/mastodon/response';
|
||||||
import type { SongInfo } from '$lib/odesliResponse';
|
import type { SongInfo } from '$lib/odesliResponse';
|
||||||
@ -17,6 +12,7 @@ export class YoutubePlaylistAdder extends OauthPlaylistAdder implements Playlist
|
|||||||
}
|
}
|
||||||
|
|
||||||
public constructAuthUrl(redirectUri: URL): URL {
|
public constructAuthUrl(redirectUri: URL): URL {
|
||||||
|
this.redirectUri = redirectUri;
|
||||||
let additionalParameters = new Map([
|
let additionalParameters = new Map([
|
||||||
['access_type', 'offline'],
|
['access_type', 'offline'],
|
||||||
['include_granted_scopes', 'false']
|
['include_granted_scopes', 'false']
|
||||||
@ -33,6 +29,7 @@ export class YoutubePlaylistAdder extends OauthPlaylistAdder implements Playlist
|
|||||||
|
|
||||||
public async receivedAuthCode(code: string, url: URL) {
|
public async receivedAuthCode(code: string, url: URL) {
|
||||||
this.logger.debug('received code');
|
this.logger.debug('received code');
|
||||||
|
this.redirectUri = url;
|
||||||
const tokenUrl = new URL('https://oauth2.googleapis.com/token');
|
const tokenUrl = new URL('https://oauth2.googleapis.com/token');
|
||||||
await this.receivedAuthCodeInternal(
|
await this.receivedAuthCodeInternal(
|
||||||
tokenUrl,
|
tokenUrl,
|
||||||
@ -41,15 +38,29 @@ export class YoutubePlaylistAdder extends OauthPlaylistAdder implements Playlist
|
|||||||
url,
|
url,
|
||||||
YOUTUBE_CLIENT_SECRET
|
YOUTUBE_CLIENT_SECRET
|
||||||
);
|
);
|
||||||
|
const token = await this.refreshToken();
|
||||||
|
if (token == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.logger.debug('Checking authorized token');
|
||||||
|
const res = await fetch(this.apiBase + '/channels?part=id&mine=true', {
|
||||||
|
method: 'GET',
|
||||||
|
headers: { Authorization: `${token.token_type} ${token.access_token}` }
|
||||||
|
}).then((r) => r.json());
|
||||||
|
this.logger.debug('Checked authorized token', res);
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.debug('Error checking authorized token', e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async refreshToken(): Promise<OauthResponse | null> {
|
private async refreshToken(force: boolean = false): Promise<OauthResponse | null> {
|
||||||
const tokenInfo = await this.shouldRefreshToken();
|
const tokenInfo = await this.shouldRefreshToken();
|
||||||
if (tokenInfo == null) {
|
if (tokenInfo == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let token = tokenInfo.token;
|
let token = tokenInfo.token;
|
||||||
if (!tokenInfo.refresh) {
|
if (!tokenInfo.refresh && !force) {
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
if (!token.refresh_token) {
|
if (!token.refresh_token) {
|
||||||
@ -62,12 +73,19 @@ export class YoutubePlaylistAdder extends OauthPlaylistAdder implements Playlist
|
|||||||
tokenUrl,
|
tokenUrl,
|
||||||
YOUTUBE_CLIENT_ID,
|
YOUTUBE_CLIENT_ID,
|
||||||
token.refresh_token,
|
token.refresh_token,
|
||||||
`${BASE_URL}/ytauth`,
|
this.getRedirectUri('ytauth').toString(),
|
||||||
YOUTUBE_CLIENT_SECRET
|
YOUTUBE_CLIENT_SECRET
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async addToPlaylist(song: SongInfo) {
|
public async addToPlaylist(song: SongInfo) {
|
||||||
|
await this.addToPlaylistRetry(song);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async addToPlaylistRetry(song: SongInfo, remaning: number = 3) {
|
||||||
|
if (remaning < 0) {
|
||||||
|
this.logger.error('max retries reached, song will not be added to spotify playlist');
|
||||||
|
}
|
||||||
this.logger.debug('addToYoutubePlaylist');
|
this.logger.debug('addToYoutubePlaylist');
|
||||||
const token = await this.refreshToken();
|
const token = await this.refreshToken();
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
@ -125,7 +143,15 @@ export class YoutubePlaylistAdder extends OauthPlaylistAdder implements Playlist
|
|||||||
const respObj = await resp.json();
|
const respObj = await resp.json();
|
||||||
this.logger.info('Added to playlist', youtubeId, song.title);
|
this.logger.info('Added to playlist', youtubeId, song.title);
|
||||||
if (respObj.error) {
|
if (respObj.error) {
|
||||||
this.logger.debug('Add to playlist failed', respObj.error.errors);
|
this.logger.error('Add to playlist failed', respObj.error.errors);
|
||||||
|
if (respObj.error.errors && respObj.error.errors[0].reason === 'authError') {
|
||||||
|
this.logger.info('Refreshing auth token');
|
||||||
|
const token = await this.refreshToken(true);
|
||||||
|
if (token == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.addToPlaylistRetry(song, remaning--);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,12 +54,16 @@ export async function saveAtomFeed(feed: Feed) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const params = new URLSearchParams();
|
const param = new FormData();
|
||||||
params.append('hub.mode', 'publish');
|
param.append('hub.mode', 'publish');
|
||||||
params.append('hub.url', `${BASE_URL}/feed.xml`);
|
param.append('hub.url', `${BASE_URL}/feed.xml`);
|
||||||
|
//const params = new URLSearchParams();
|
||||||
|
//params.append('hub.mode', 'publish');
|
||||||
|
//params.append('hub.url', `${BASE_URL}/feed.xml`);
|
||||||
await fetch(WEBSUB_HUB, {
|
await fetch(WEBSUB_HUB, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: params
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||||
|
body: param
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Failed to update WebSub hub', e);
|
logger.error('Failed to update WebSub hub', e);
|
||||||
|
@ -2,6 +2,7 @@ import {
|
|||||||
HASHTAG_FILTER,
|
HASHTAG_FILTER,
|
||||||
MASTODON_ACCESS_TOKEN,
|
MASTODON_ACCESS_TOKEN,
|
||||||
MASTODON_INSTANCE,
|
MASTODON_INSTANCE,
|
||||||
|
IGNORE_USERS,
|
||||||
ODESLI_API_KEY,
|
ODESLI_API_KEY,
|
||||||
YOUTUBE_API_KEY
|
YOUTUBE_API_KEY
|
||||||
} from '$env/static/private';
|
} from '$env/static/private';
|
||||||
@ -36,6 +37,7 @@ import sharp from 'sharp';
|
|||||||
import { URL, URLSearchParams } from 'url';
|
import { URL, URLSearchParams } from 'url';
|
||||||
import { WebSocket } from 'ws';
|
import { WebSocket } from 'ws';
|
||||||
import type { PlaylistAdder } from './playlist/playlistAdder';
|
import type { PlaylistAdder } from './playlist/playlistAdder';
|
||||||
|
import { TidalPlaylistAdder } from './playlist/tidalPlaylistAdder';
|
||||||
|
|
||||||
const URL_REGEX = new RegExp(/href="(?<postUrl>[^>]+?)" target="_blank"/gm);
|
const URL_REGEX = new RegExp(/href="(?<postUrl>[^>]+?)" target="_blank"/gm);
|
||||||
const INVIDIOUS_REGEX = new RegExp(/invidious.*?watch.*?v=(?<videoId>[a-zA-Z_0-9-]+)/gm);
|
const INVIDIOUS_REGEX = new RegExp(/invidious.*?watch.*?v=(?<videoId>[a-zA-Z_0-9-]+)/gm);
|
||||||
@ -48,6 +50,7 @@ export class TimelineReader {
|
|||||||
private lastPosts: string[] = [];
|
private lastPosts: string[] = [];
|
||||||
private playlistAdders: PlaylistAdder[];
|
private playlistAdders: PlaylistAdder[];
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
|
private ignoredUsers: string[];
|
||||||
|
|
||||||
private async isMusicVideo(videoId: string) {
|
private async isMusicVideo(videoId: string) {
|
||||||
if (!YOUTUBE_API_KEY || YOUTUBE_API_KEY === 'CHANGE_ME') {
|
if (!YOUTUBE_API_KEY || YOUTUBE_API_KEY === 'CHANGE_ME') {
|
||||||
@ -174,22 +177,24 @@ export class TimelineReader {
|
|||||||
}
|
}
|
||||||
const isMusic = await this.isMusicVideo(youtubeId);
|
const isMusic = await this.isMusicVideo(youtubeId);
|
||||||
if (!isMusic) {
|
if (!isMusic) {
|
||||||
this.logger.debug('Probably not a music video', youtubeId, url);
|
this.logger.debug('Probably not a music video', youtubeId);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const spotify: Platform = 'spotify';
|
const spotify: Platform = 'spotify';
|
||||||
|
const tidal: Platform = 'tidal';
|
||||||
|
const tidalId = odesliInfo.linksByPlatform[tidal]?.entityUniqueId;
|
||||||
|
const tidalUri = tidalId ? odesliInfo.entitiesByUniqueId[tidalId].id : undefined;
|
||||||
|
|
||||||
const songInfo = {
|
const songInfo = {
|
||||||
...info,
|
...info,
|
||||||
pageUrl: odesliInfo.pageUrl,
|
pageUrl: odesliInfo.pageUrl,
|
||||||
youtubeUrl: odesliInfo.linksByPlatform[platform]?.url,
|
youtubeUrl: odesliInfo.linksByPlatform[platform]?.url,
|
||||||
spotifyUrl: odesliInfo.linksByPlatform[spotify]?.url,
|
spotifyUrl: odesliInfo.linksByPlatform[spotify]?.url,
|
||||||
spotifyUri: odesliInfo.linksByPlatform[spotify]?.nativeAppUriDesktop,
|
spotifyUri: odesliInfo.linksByPlatform[spotify]?.nativeAppUriDesktop,
|
||||||
|
tidalUri: tidalUri,
|
||||||
postedUrl: url.toString()
|
postedUrl: url.toString()
|
||||||
} as SongInfo;
|
} as SongInfo;
|
||||||
if (songInfo.youtubeUrl && !songInfo.spotifyUrl) {
|
|
||||||
this.logger.warn('SongInfo with YT, but no spotify URL', odesliInfo);
|
|
||||||
}
|
|
||||||
return songInfo;
|
return songInfo;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error && e.cause === 429) {
|
if (e instanceof Error && e.cause === 429) {
|
||||||
@ -372,6 +377,18 @@ export class TimelineReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async checkAndSavePost(post: Post) {
|
private async checkAndSavePost(post: Post) {
|
||||||
|
const isIgnored = this.ignoredUsers.includes(post.account.acct);
|
||||||
|
if (isIgnored) {
|
||||||
|
this.logger.info(
|
||||||
|
'Ignoring post by ignored user',
|
||||||
|
post.account.acct,
|
||||||
|
'is ignored',
|
||||||
|
this.ignoredUsers,
|
||||||
|
isIgnored
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const hashttags: string[] = HASHTAG_FILTER.split(',');
|
const hashttags: string[] = HASHTAG_FILTER.split(',');
|
||||||
const found_tags: Tag[] = post.tags.filter((t: Tag) => hashttags.includes(t.name));
|
const found_tags: Tag[] = post.tags.filter((t: Tag) => hashttags.includes(t.name));
|
||||||
|
|
||||||
@ -405,11 +422,42 @@ export class TimelineReader {
|
|||||||
const socket = new WebSocket(
|
const socket = new WebSocket(
|
||||||
`wss://${MASTODON_INSTANCE}/api/v1/streaming?type=subscribe&stream=public:local&access_token=${MASTODON_ACCESS_TOKEN}`
|
`wss://${MASTODON_INSTANCE}/api/v1/streaming?type=subscribe&stream=public:local&access_token=${MASTODON_ACCESS_TOKEN}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Sometimes, the app just stops receiving WS updates.
|
||||||
|
// Regularly check if it is necessary to reset it
|
||||||
|
const wsTimeout = 5;
|
||||||
|
let timeoutId = setTimeout(
|
||||||
|
() => {
|
||||||
|
socketLogger.warn(
|
||||||
|
'Websocket has not received a new post in',
|
||||||
|
wsTimeout,
|
||||||
|
'hours. Resetting, it might be stuck'
|
||||||
|
);
|
||||||
|
socket.close();
|
||||||
|
this.startWebsocket();
|
||||||
|
},
|
||||||
|
1000 * 60 * 60 * wsTimeout
|
||||||
|
); // 5 hours
|
||||||
socket.onopen = () => {
|
socket.onopen = () => {
|
||||||
socketLogger.log('Connected to WS');
|
socketLogger.log('Connected to WS');
|
||||||
};
|
};
|
||||||
socket.onmessage = async (event) => {
|
socket.onmessage = async (event) => {
|
||||||
try {
|
try {
|
||||||
|
// Reset timer
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
timeoutId = setTimeout(
|
||||||
|
() => {
|
||||||
|
socketLogger.warn(
|
||||||
|
'Websocket has not received a new post in',
|
||||||
|
wsTimeout,
|
||||||
|
'hours. Resetting, it might be stuck'
|
||||||
|
);
|
||||||
|
socket.close();
|
||||||
|
this.startWebsocket();
|
||||||
|
},
|
||||||
|
1000 * 60 * 60 * wsTimeout
|
||||||
|
);
|
||||||
|
|
||||||
const data: TimelineEvent = JSON.parse(event.data.toString());
|
const data: TimelineEvent = JSON.parse(event.data.toString());
|
||||||
socketLogger.debug('ES event', data.event);
|
socketLogger.debug('ES event', data.event);
|
||||||
if (data.event !== 'update') {
|
if (data.event !== 'update') {
|
||||||
@ -479,7 +527,21 @@ export class TimelineReader {
|
|||||||
private constructor() {
|
private constructor() {
|
||||||
this.logger = new Logger('Timeline');
|
this.logger = new Logger('Timeline');
|
||||||
this.logger.log('Constructing timeline object');
|
this.logger.log('Constructing timeline object');
|
||||||
this.playlistAdders = [new YoutubePlaylistAdder(), new SpotifyPlaylistAdder()];
|
this.playlistAdders = [
|
||||||
|
new YoutubePlaylistAdder(),
|
||||||
|
new SpotifyPlaylistAdder(),
|
||||||
|
new TidalPlaylistAdder()
|
||||||
|
];
|
||||||
|
this.ignoredUsers =
|
||||||
|
IGNORE_USERS === undefined || IGNORE_USERS === 'CHANGE_ME' || !!IGNORE_USERS
|
||||||
|
? []
|
||||||
|
: IGNORE_USERS.split(',')
|
||||||
|
.map((u) => (u.startsWith('@') ? u.substring(1) : u))
|
||||||
|
.map((u) =>
|
||||||
|
u.endsWith('@' + MASTODON_INSTANCE)
|
||||||
|
? u.substring(0, u.length - ('@' + MASTODON_INSTANCE).length)
|
||||||
|
: u
|
||||||
|
);
|
||||||
this.startWebsocket();
|
this.startWebsocket();
|
||||||
|
|
||||||
this.loadPostsSinceLastRun()
|
this.loadPostsSinceLastRun()
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
export function isTruthy(value: string | number | boolean | null | undefined): boolean {
|
export function isTruthy(value: string | number | boolean | null | undefined): boolean {
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
return value.toLowerCase() === 'true' || !!+value; // here we parse to number first
|
return value.toLowerCase() === 'true' || !!+value; // here we parse to number first
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
position: sticky;
|
position: sticky;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
z-index: 99;
|
||||||
}
|
}
|
||||||
:global(.toast.error) {
|
:global(.toast.error) {
|
||||||
--toastColor: var(--color-button-text);
|
--toastColor: var(--color-button-text);
|
||||||
|
@ -100,7 +100,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(() => {
|
||||||
posts = data.posts;
|
posts = data.posts;
|
||||||
if (posts.length > 0) {
|
if (posts.length > 0) {
|
||||||
oldestBeforeLastFetch = new Date(posts[posts.length - 1].created_at).getTime();
|
oldestBeforeLastFetch = new Date(posts[posts.length - 1].created_at).getTime();
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import type { Post } from '$lib/mastodon/response';
|
import type { Post } from '$lib/mastodon/response';
|
||||||
import type { PageLoad } from './$types';
|
import type { PageLoad } from './$types';
|
||||||
|
|
||||||
export const load = (async ({ fetch }) => {
|
export const load = (async ({ fetch, setHeaders }) => {
|
||||||
const p = await fetch('/');
|
const p = await fetch('/');
|
||||||
|
setHeaders({
|
||||||
|
'cache-control': 'public,max-age=60'
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
posts: (await p.json()) as Post[]
|
posts: (await p.json()) as Post[]
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import type { RequestHandler } from './$types';
|
import type { RequestHandler } from './$types';
|
||||||
|
|
||||||
export const GET = (async ({ fetch }) => {
|
export const GET = (async ({ fetch, setHeaders }) => {
|
||||||
|
setHeaders({
|
||||||
|
'cache-control': 'max-age=10'
|
||||||
|
});
|
||||||
return await fetch('api/posts');
|
return await fetch('api/posts');
|
||||||
}) satisfies RequestHandler;
|
}) satisfies RequestHandler;
|
||||||
|
@ -1,19 +1,31 @@
|
|||||||
|
import { BASE_URL } from '$env/static/private';
|
||||||
import { Logger } from '$lib/log';
|
import { Logger } from '$lib/log';
|
||||||
import { SpotifyPlaylistAdder } from '$lib/server/playlist/spotifyPlaylistAdder';
|
import { SpotifyPlaylistAdder } from '$lib/server/playlist/spotifyPlaylistAdder';
|
||||||
import { redirect } from '@sveltejs/kit';
|
import { redirect } from '@sveltejs/kit';
|
||||||
import type { PageServerLoad } from './$types';
|
import type { PageServerLoad } from './$types';
|
||||||
|
const { DEV } = import.meta.env;
|
||||||
|
|
||||||
const logger = new Logger('SpotifyAuth');
|
const logger = new Logger('SpotifyAuth');
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ url }) => {
|
export const load: PageServerLoad = async ({ url, request }) => {
|
||||||
|
const forwardedHost = request.headers.get('X-Forwarded-Host');
|
||||||
|
let redirect_base;
|
||||||
|
if (DEV) {
|
||||||
|
redirect_base = url.origin;
|
||||||
|
} else if (forwardedHost) {
|
||||||
|
redirect_base = `${url.protocol}//${forwardedHost}`;
|
||||||
|
} else {
|
||||||
|
redirect_base = BASE_URL;
|
||||||
|
}
|
||||||
|
const redirect_uri = new URL(`${redirect_base}${url.pathname}`);
|
||||||
|
|
||||||
const adder = new SpotifyPlaylistAdder();
|
const adder = new SpotifyPlaylistAdder();
|
||||||
let redirectUri = url;
|
if (url.hostname === 'localhost' && DEV) {
|
||||||
if (url.hostname === 'localhost') {
|
redirect_uri.hostname = '127.0.0.1';
|
||||||
redirectUri.hostname = '127.0.0.1';
|
|
||||||
}
|
}
|
||||||
logger.debug(url.searchParams, url.hostname);
|
logger.debug(url.searchParams, url.hostname);
|
||||||
if (url.searchParams.has('code')) {
|
if (url.searchParams.has('code')) {
|
||||||
await adder.receivedAuthCode(url.searchParams.get('code') || '', url);
|
await adder.receivedAuthCode(url.searchParams.get('code') || '', redirect_uri);
|
||||||
redirect(307, '/');
|
redirect(307, '/');
|
||||||
} else if (url.searchParams.has('error')) {
|
} else if (url.searchParams.has('error')) {
|
||||||
logger.error('received error', url.searchParams.get('error'));
|
logger.error('received error', url.searchParams.get('error'));
|
||||||
@ -24,7 +36,7 @@ export const load: PageServerLoad = async ({ url }) => {
|
|||||||
redirect(307, '/');
|
redirect(307, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
const authUrl = adder.constructAuthUrl(url);
|
const authUrl = adder.constructAuthUrl(redirect_uri);
|
||||||
logger.debug('+page.server.ts', authUrl.toString());
|
logger.debug('+page.server.ts', authUrl.toString());
|
||||||
redirect(307, authUrl);
|
redirect(307, authUrl);
|
||||||
};
|
};
|
||||||
|
39
src/routes/tidalAuth/+page.server.ts
Normal file
39
src/routes/tidalAuth/+page.server.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { BASE_URL } from '$env/static/private';
|
||||||
|
import { Logger } from '$lib/log';
|
||||||
|
import { redirect } from '@sveltejs/kit';
|
||||||
|
import type { PageServerLoad } from './$types';
|
||||||
|
import { TidalPlaylistAdder } from '$lib/server/playlist/tidalPlaylistAdder';
|
||||||
|
import { URL } from 'node:url';
|
||||||
|
const { DEV } = import.meta.env;
|
||||||
|
|
||||||
|
const logger = new Logger('TidalAuth');
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async ({ url, request }) => {
|
||||||
|
const forwardedHost = request.headers.get('X-Forwarded-Host');
|
||||||
|
let redirect_base;
|
||||||
|
if (DEV) {
|
||||||
|
redirect_base = url.origin;
|
||||||
|
} else if (forwardedHost) {
|
||||||
|
redirect_base = `${url.protocol}//${forwardedHost}`;
|
||||||
|
} else {
|
||||||
|
redirect_base = BASE_URL;
|
||||||
|
}
|
||||||
|
const redirect_uri = new URL(`${redirect_base}${url.pathname}`);
|
||||||
|
const adder = new TidalPlaylistAdder();
|
||||||
|
logger.debug(url.searchParams, url.hostname, redirect_uri);
|
||||||
|
if (url.searchParams.has('code')) {
|
||||||
|
await adder.receivedAuthCode(url.searchParams.get('code') || '', redirect_uri);
|
||||||
|
redirect(307, '/');
|
||||||
|
} else if (url.searchParams.has('error')) {
|
||||||
|
logger.error('received error', url.searchParams.get('error'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await adder.authCodeExists()) {
|
||||||
|
redirect(307, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
const authUrl = adder.constructAuthUrl(redirect_uri);
|
||||||
|
logger.debug('+page.server.ts', authUrl.toString());
|
||||||
|
redirect(307, authUrl);
|
||||||
|
};
|
1
src/routes/tidalAuth/+page.svelte
Normal file
1
src/routes/tidalAuth/+page.svelte
Normal file
@ -0,0 +1 @@
|
|||||||
|
<h1>Something went wrong</h1>
|
@ -1,15 +1,29 @@
|
|||||||
|
import { BASE_URL } from '$env/static/private';
|
||||||
import { Logger } from '$lib/log';
|
import { Logger } from '$lib/log';
|
||||||
import { YoutubePlaylistAdder } from '$lib/server/playlist/ytPlaylistAdder';
|
import { YoutubePlaylistAdder } from '$lib/server/playlist/ytPlaylistAdder';
|
||||||
import { redirect } from '@sveltejs/kit';
|
import { redirect } from '@sveltejs/kit';
|
||||||
import type { PageServerLoad } from './$types';
|
import type { PageServerLoad } from './$types';
|
||||||
|
const { DEV } = import.meta.env;
|
||||||
|
|
||||||
const logger = new Logger('YT Auth');
|
const logger = new Logger('YT Auth');
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ url }) => {
|
export const load: PageServerLoad = async ({ url, request }) => {
|
||||||
|
const forwardedHost = request.headers.get('X-Forwarded-Host');
|
||||||
|
let redirect_base;
|
||||||
|
if (DEV) {
|
||||||
|
redirect_base = url.origin;
|
||||||
|
} else if (forwardedHost) {
|
||||||
|
redirect_base = `${url.protocol}//${forwardedHost}`;
|
||||||
|
} else {
|
||||||
|
redirect_base = BASE_URL;
|
||||||
|
}
|
||||||
|
const redirect_uri = new URL(`${redirect_base}${url.pathname}`);
|
||||||
|
|
||||||
const adder = new YoutubePlaylistAdder();
|
const adder = new YoutubePlaylistAdder();
|
||||||
|
logger.debug('redirect URL', redirect_uri);
|
||||||
if (url.searchParams.has('code')) {
|
if (url.searchParams.has('code')) {
|
||||||
logger.debug(url.searchParams);
|
logger.debug(url.searchParams);
|
||||||
await adder.receivedAuthCode(url.searchParams.get('code') || '', url);
|
await adder.receivedAuthCode(url.searchParams.get('code') || '', redirect_uri);
|
||||||
redirect(307, '/');
|
redirect(307, '/');
|
||||||
} else if (url.searchParams.has('error')) {
|
} else if (url.searchParams.has('error')) {
|
||||||
logger.error('received error', url.searchParams.get('error'));
|
logger.error('received error', url.searchParams.get('error'));
|
||||||
@ -19,8 +33,7 @@ export const load: PageServerLoad = async ({ url }) => {
|
|||||||
if (await adder.authCodeExists()) {
|
if (await adder.authCodeExists()) {
|
||||||
redirect(307, '/');
|
redirect(307, '/');
|
||||||
}
|
}
|
||||||
|
const authUrl = adder.constructAuthUrl(redirect_uri);
|
||||||
const authUrl = adder.constructAuthUrl(url);
|
|
||||||
logger.debug('+page.server.ts', authUrl.toString());
|
logger.debug('+page.server.ts', authUrl.toString());
|
||||||
redirect(307, authUrl);
|
redirect(307, authUrl);
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
. /home/moshing-mammut/.nvm/nvm.sh
|
. /home/moshing-mammut/.nvm/nvm.sh
|
||||||
node -r dotenv/config build
|
node -r dotenv/config build &
|
@ -12,7 +12,9 @@ const config = {
|
|||||||
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
|
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
|
||||||
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
||||||
adapter: adapter(),
|
adapter: adapter(),
|
||||||
|
version: {
|
||||||
|
name: process.env.npm_package_version
|
||||||
|
},
|
||||||
csp: {
|
csp: {
|
||||||
directives: {
|
directives: {
|
||||||
'script-src': ['self', 'unsafe-inline'],
|
'script-src': ['self', 'unsafe-inline'],
|
||||||
|
Reference in New Issue
Block a user